質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

5回答

7309閲覧

JavaScriptは値渡しか?共有渡し(参照の値渡し)か?

raccy

総合スコア21735

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

1グッド

12クリップ

投稿2015/08/14 17:14

JavaScriptは値渡しなのでしょうか?共有渡し(参照の値渡し)なのでしょうか?普通のObjectはすべて共有渡しだと思うのですが、プリミティブ型(?)については違うようなのです。しかし、StackOverflow(※)でもまとまっていないようで、よくわかりませんでした。詳細を分かる方がいれば、下記について教えて下さい。
※参考: StackOverflow: Is JavaScript a pass-by-reference or pass-by-value language?

  1. 値渡しになる型はあるのでしょうか?ある場合は、どの型がなるのでしょうか?

  2. 1.の型のうちmutableな型はあるのでしょうか?ある場合は、mutableな型の破壊的メソッドはどのようなものがあるのでしょうか?

  3. 値渡しであることが分かる(共有渡しでは説明できない)コードはありますでしょうか?

  4. C++やC#で言う所の参照渡しも存在するのでしょうか?その場合はどのような物が参照渡しになるのでしょうか?

  5. ECMAScript2015仕様書MDN JavaScriptなどの公式または公式に準ずる資料で上のことに言及している部分はどこになるのでしょうか?

私の考えでは、インクリメント/デクリメント(++/--)を特殊なx+=1/x-=1であると解釈すれば、すべて共有渡しと解釈して問題ないのではないか?と思っています。反例があれば教えて欲しいです。

【注記】
ここでの値渡しはcall-by-value、参照渡しはcall-by-referenc、共有渡し(参照の値渡し)はcall-by-sharingを意味します。違いについては「値渡しと参照渡しの違いを理解する」の記事を参考にして下さい。回答の際は厳密に用語を分けていただきますようお願いします。

juner👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

実装面で見るならば、Reference 型を「参照の値渡し」、Primitive 型 (ToPrimitive で値に変化がない型)を「値渡し」と呼称して良いと思います。

JavaScript

1function foo (a) { 2 a[0] = 1; 3 a = {}; 4} 5 6var array = [], string = '0'; 7foo(array); 8foo(string); 9console.log(array); // [1] (参照の値渡し) 10console.log(string); // "0" (値渡し)

ただし、ECMAScript 仕様では「値渡し」「参照の値渡し」には言及されてなく、実装面の意味合いが強いと考えています。
詳細 ECMA-262-3 第8章 評価戦略 - mixi Engineers' Blogが比較的詳しいですが、説明をぼかして書いている部分も見受けられます。

MDN ではプリミティブなパラメータ (数値など) は値渡しで関数に渡されますとありますが、厳密な定義でいうなら MDN は間違っている解説も多いので参考程度に留めるのが良いと思います(解説が古かったり、英語解説だけに載っていたり、用語が間違っていたりする記述を何度も見ています)。

投稿2015/08/14 23:52

編集2015/08/14 23:54
think49

総合スコア18162

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2015/08/15 00:32

とても参考になる資料で、ありがとうございます。 immutableな場合は値渡しと共有渡し(参照の値渡し)で違いが出ないため、 実装依存という側面が強いと言うことでしょうか。 あと、例ですけど、 string = '0'; string[0] = 1; console.log(string); // "0" ??? となるので、たとえStringが共有渡し(参照の値渡し)であっても、 問題なかったりします。 Stringはプリミティブ型ですが、本当に値渡しなのか、 本当に文字列全てを値として渡すなんて非効率なことをしているのか、 そこら辺がすごく気になるところなのです。 V8やSpidermonkeyのソースコードを見るしか無いんでしょうか…。
think49

2015/08/15 01:02

string[0] は非破壊的だったのですね。失礼しました。 改めて考えてみると String.prototype の中で破壊的メソッドが思いつきませんね。 憶測ですが、「実は全てが『参照の値渡し』であって、Primitive 型には破壊的機構が存在しないから『値渡し』のように見えているのかもしれない」とふと思いました。
guest

0

一言で言うなら、全て共有渡しです。
(ここでの共有渡しとは、値の実体を複製するのではなく、その存在を変数同士で共有するように渡すこと、
ただし参照の値渡しのような具体的な実装に踏み込んだものとも違って、
何かを渡すというよりむしろ、1つの値に別の名前を付けさせるという方向性(最も仕様の流れに近い)の概念とします。)

仕様ではプリミティブ型とオブジェクト型で渡され方が変わるようには書いていませんし、
オブジェクト値の実体が参照だとも書かれていません。
ならば全てが共有渡しと見るべきです。

そもそも渡され方は渡し方で決まるべきで、渡される値によって異なると言うのは不自然です。
JSでは渡し方はひと通りですから、渡され方もひと通りなのです。

因みに仕様のReference型は変数の解決における内部処理に使われるものであって、
紛らわしいですが渡され方の問題とは軸がズレていて本質的には関係ないものです。

投稿2015/08/15 10:29

編集2015/08/15 10:41
jser

総合スコア100

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2015/08/15 11:31

プリミティブ型が全てimmutableであれば、値渡しと共有渡しの違いは コード上に現れないため、全て共有渡しと見なせる事には同意します。 ただ、自分で書いていてなんですが、"++"等を考えるとNumberはimmutableなのか? という疑問もあるのです。 また、Javaは渡し方は一つしかありませんけど、プリミティブ型は値渡しで、 参照型は共有渡し(参照の値渡し)とはっきりとした違いがあります。 Rubyなんかはもっとおかしくて、普通のオブジェクトが共有渡しなのに、 Fixnumとか一部の即値はCでの実装レベルでは値渡しになっています。 ※RubyのFixnumはimmutableなので、コードレベルでの違いは見えません。 渡し方が一種類だから渡され方も一種類は強引のような気がします。
raccy

2015/08/15 11:47

もうひとつ。 jserさんの「共有渡し」の説明ですと、C++やC#の参照渡しになると思います。 C++の参照渡しはまさしく変数の別名であり、同じ領域を「共有」するものです。 その意味で「共有渡し」と言っているのであれば、C++の参照渡しと同じ事が できるはずですが、JavaScriptでそのようなことはできません。 jserさんがいう「共有渡し」について、詳しく書かれた資料等があれば、 教えていただけますでしょうか?
jser

2015/08/15 13:24

numberはimmutableです。 ++等はただ1を足した新しい値を作って変数に格納しなおしているだけで、元々変数に格納されていた数値に影響は与えていません。 また、私が言っているのは参照渡しとは違います。 参照渡しとは「変数」を共有することで、私が言っているのは値を複数変数間で共有すること、 つまり変数自体ではなく変数を解決した際の値が共有されている状態を言っています。 更に言い換えると、渡すというよりすでに存在する値に対して別の名前を与える行為でもあるのです。
raccy

2015/08/18 09:44

`++`の動きを私も確認してみましたが、"1"とかまでインクリメントできてしまうし、 C/C++系と同じように考えていた自分がいけなかったようです。 jserさんのおっしゃる共有渡しについては、すいません、私が勘違いしていたようです。 JavaScriptはC/C++系の文法を取っているから、C/C++系と同じように、 変数には何らかの値を保存する領域が紐付いていて値が入っているという イメージを勝手に持っていました。 その値を共有って事はその領域(つまり変数そのもの)を共有だから、 参照渡しと同じじゃ無いかって、勘違いしていました。 変数と値がある領域を切り離して考えれば、言っている意味がわかりました。
jser

2015/08/18 10:37

結局仕様にはどうあるのかというと、ひたすら「A is B」の積み重ねです。 これをただ「Bを複製してAに格納する」と解釈すると、実際の挙動と差が出るので、 これは「BをAと置く」、つまり値に新たな名前が付けられると解釈することになるのです。 もしくは、格納されるのは暗黙的に必ず参照の値であって、しかるべきときに解決されると捉えても良いです。 前者が「共有渡し」で、後者が「参照の値渡し」にあたります。 並べると分かる通り実はこの2つは具体度が違うだけの同じ概念なのですが、 実装に踏み込んでいるかいないかという、それこそが大きな違いでもあります。 要は「参照の値」という仕様に出てこないものを使わず「参照の値渡し」と同等のことを表現する上手い言葉が「共有渡し」なのです。
guest

0

(私が初歩的な勘違いをしている気がするのですが)もし全てが共有渡しの場合、以下の例では全て2が出力されると思うのですが、これはbが値渡しである実証例にはならないでしょうか?

javascript

1function foo (b,d) { 2 a = 2; 3 c[0] = 2; 4 console.log(b); //1 5 console.log(d[0]); //2 6} 7 8a =1,c=[1,0]; 9foo(a,c); 10console.log(a); //2 11console.log(c[0]); //2

投稿2015/08/15 12:04

hirohiro

総合スコア2068

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2015/08/15 12:37

"="の代入は中にある値を変化させるのでなく、箱を丸ごと入れ直しなので、 共有渡しでも結果はおなじになります。 最初のを`a={a:2}`、次のを`a={a:1}`などにしてみて見比べてみて下さい。
hirohiro

2015/08/15 16:27

ありがとうございます。なるほど勉強になります。質問者さんへの逆質問になってしまってすみません。
guest

0

何度もすみません。
一応読み直して基本的な疑問の答えにはなるかと思うので、検索に掛かったページを挙げておきます。

11.2. By Value Versus by Reference - JavaScript: The Definitive Guide, 4th Edition.

参考になれば幸いです。

投稿2015/08/14 21:38

編集2015/08/14 21:49
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2015/08/14 23:58

SOの質問者もそうですが、"by reference"と言っている時点で、 その文章の作者のレベルが低いと思います。 それに比較を曖昧で役立たずな`==`でやってる時点でおかしいですし、 valueの例(Example 11-1)は変数そのものへの代入なのに、 referenceの例(Example 11-2)は変数そのものへの代入じゃ無くて プロパティへの代入とか意味が異なる事やっているし、 その後変数そのものへの代入の例(Example 11-3)とか示して、 "by reference"の意味について言い訳しているようですが、 用語の使い方で読者(特にC++やC#ユーザー)を混乱するだけです。 まともな技術書が多いO'Reilly出版とは言え、信頼できる資料とは言いにくいです。
think49

2015/08/15 00:08

横から失礼します。 参考までにお伺いしたいのですが、「SOの質問者」とは「Stack Overflow の質問者」という意味でしょうか。
退会済みユーザー

退会済みユーザー

2015/08/15 00:26

O'Reillyの本も玉石混交なので信用度は謎だったのですが、参考にならない情報で申し訳ない。
raccy

2015/08/15 00:37

>> think49さん SO=StackOverflowです。すいません、そう略す人がおおかったもので、 私もなんもきにせず略しちゃいました。 >> pickovoldさん いえいえ、資料は多いだけうれしいので、多少胡散臭いものであっても、 とても参考になりました。
think49

2015/08/15 01:05

raccyさん やはり、StackOverflow の略称だったのですね。何となくそうかなとは思ったのですが、略称について調べる手段を思いつかず…。参考になりました。
guest

0

まず最初に、言語の仕様に、『値渡しや参照渡し、あるいは共有渡しを使うのか』を明記する必要もなければ理由もありません(言語によるでしょうが少なくともJavaScriptでは必要ないと思われる)。

なぜかというと、『値渡しや参照渡し、あるいは共有渡しを使うのか』という話は、実装の問題だからです。
仕様では、変数の渡し方に関わらず、どういうコードを書いたらどういう結果になるのかだけ決めればよいだけなのです。

どのように変数の値を渡したらメモリ効率がよいのか、あるいは速度が速いのかなどは何を優先するかによっても変わってくるので、実装する側の人間が決めれば良い話なので仕様側で決める必要はありません(別に決めてしまっても良い)。

そこで改めて質問の趣旨を考えてみると、

1.ECMAScriptの仕様で『値渡しや参照渡し、あるいは共有渡しを使うのか』が決められているのか?
2.V8などの実際の実装で『値渡しや参照渡し、あるいは共有渡しを使うのか』がどうなっているのか?

という2種類の疑問がごっちゃになっているように思われます。

1のほうであれば、仕様を全部読めば、決められているのかいないのかははっきりすると思います。
(たぶん決められていなさそう…)

また、2のほうであれば、実際の実装のソースを読むしかないと思います。ただし、仮にV8の実装では共有渡しだったとしても、他の実装では違う可能性もあるので、すべての実装でどうなっているのか知りたいのならば、片っぱしからソースを読む必要が出てくるので現実的ではないと思われます。

投稿2015/08/18 06:30

JunSuzukiJapan

総合スコア310

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jser

2015/08/18 06:49

1について言うと、参照渡しはもちろん違うことが分かります。 そして、「値渡し」ですがこれは定義にもよりますがWikipedia等を参考にすると 値を複製して渡すというニュアンスが強いと思われます。 この意味だとオブジェクトとプリミティブで扱いを分けなければなりません。 しかし仕様では扱いを分けてはいないので、例え結果的に同じだとしてもわざわざ プリミティブは値渡し、オブジェクトは参照の値渡しのように分けるのは仕様上で語るのならば不自然だと思います。 よって、共有渡しと言うのが、「~渡し」という限りでは一番妥当だろうというのが自分の考えです。 「共有渡し」は別の言語で広く使われていない単語であるので意味を調整しやすいということもあります。 2について言うと、これは各エンジンの最もインタプリタに近い層と、各最適化層での抽象段階、そしてマシン語では構造と概念が違ってくるでしょうからあまり語っても意味が無いと思いますが、 V8の分かりやすい部分でいうと、すべての値はポインタによって間接的にアクセスますが、小さな整数(SMI)のみは値がポインタに埋め込まれて直接利用されますから、 SMIは値渡し、他は参照の値渡しということになるのでしょう。
JunSuzukiJapan

2015/08/19 02:34 編集

値渡しについてですが、「言語において、なぜ値渡しや参照渡しに分かれているのか(分かれたのか)」について知っておくと理解が深まるのかなと思うのでちょっと説明してみますね。 以下は元の質問とはかなり趣旨がはずれるので興味がなければ読まなくて結構です。 そもそも変数の値を渡すのに、値渡しやら参照渡しやらに分かれたのはなぜなのか? 必要がなければ、変数の渡し方は1種類で済むはずです。 ですが、分かれている(分かれた)のにはそれなりの理由があります。 誤解をおそれずに一言で言うと、「値がCPUのレジスタに収まるかどうか」で分かれています。 (ここでは関数型言語など、「元の値が変更可能かどうか」で値渡しかどうかを区別するような言語は考えないこととします。) C言語で考えると、intやchar型はレジスタに収まる(収まるようにビット幅が制限されている)ので値渡しで済みます。 配列や構造体などはレジスタに収まらないので、メモリ上におかれたアドレスを受け渡すことしかできないので、アドレス(ポインタ)を渡すしかなくなります。これは、値渡し(レジスタですべての内容を渡すこと)ができないので「仕方がなく」そういう仕様にするしかないのです。 例えば、C言語における文字列はchar型の配列として表されるので、値渡しではなく参照(ポインタ)を渡すしかなかったのです。 値渡しや参照渡しが、元々は「元の値を変更可能かどうか」から生まれた概念ではなく、「CPUのレジスタで渡せるかどうか」という実装上の都合から生まれたものだというのを念頭において考えてみるとより理解が深まるのではないか思ったのでちょっと書いてみました。
raccy

2015/08/20 10:18

C言語では、どんなサイズの構造体でも普通に値渡しできるんですけど。 https://paiza.io/projects/Urb4hR2P0T7lgCiFBCwo3A 上のhoge関数への渡し方って実は値渡しでは無かったということでしょうか?
JunSuzukiJapan

2015/08/20 21:46 編集

その例ですと、値渡しで間違いないですね。 スタックに構造体すべての値をコピーして渡す方法になるので、効率を考えるとあまり使わない気はしますが、C言語ってそういえばそういう仕様でしたね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問