JavaScriptで変数の割り当てはどのように機能しますか?


97

先日、JavaScriptで大量割り当てがどのように機能するかを正確に確認するために遊んでいました。

まず、コンソールでこの例を試しました:

a = b = {};
a.foo = 'bar';
console.log(b.foo);

その結果、アラートに「バー」が表示されていました。それは十分に公平でaありb、実際には同じオブジェクトの単なるエイリアスです。次に、この例をもっと簡単にするにはどうすればよいかと考えました。

a = b = 'foo';
a = 'bar';
console.log(b);

それはだいたい同じですよね。さて今回は、最初の例の振る舞いから期待するほどには戻りfooませんbar

なぜこれが起こるのですか?

注:この例は、次のコードでさらに簡略化できます。

a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);

a = 'foo';
b = a;
a = 'bar';
console.log(b);

(JavaScriptは文字列や整数などのプリミティブをハッシュとは異なる方法で処理していると思います。ハッシュはポインターを返しますが、「コア」プリミティブはそれらのコピーを返します)


ここで割り当てを説明する:syntaxsuccess.com/viewarticle/...
TGH

回答:


114

最初の例では、既存のオブジェクトのプロパティを設定しています。2番目の例では、まったく新しいオブジェクトを割り当てています。

a = b = {};

aそしてb今、同じオブジェクトへのポインタです。だからあなたがするとき:

a.foo = 'bar';

それb.foo以降も設定され、同じオブジェクトab指します。

しかしながら!

代わりにこれを行う場合:

a = 'bar';

今はa別のオブジェクトを指していると言っています。これはa以前の指摘には影響しません。

JavaScriptでは、変数の割り当てとプロパティの割り当ては2つの異なる操作です。変数はオブジェクトへのポインタと考えるのが最適です。変数に直接割り当てる場合、オブジェクトを変更するのではなく、変数を別のオブジェクトに再ポイントするだけです。

ただし、などのプロパティを割り当てると、ポイントするa.fooオブジェクトが変更さaれます。もちろん、これは、すべてが同じオブジェクトを指しているという理由だけで、このオブジェクトを指している他のすべての参照も変更します。


3
「あなたは今ポイントが別のオブジェクトを指していると言っています。」いいえ、単語オブジェクトを使用しないでください。文字列はJavaScriptのオブジェクトではありません。
Nosredna 2009

9
文字列は技術的にはJavaScriptタイプの「オブジェクト」ではないかもしれませんが、オブジェクト指向の意味でのオブジェクトと考えることができます。
アレックスウェイン

2
@Squeegy:文字列はプリミティブであり、オブジェクトではありません。文字列に任意のプロパティを割り当てることはできません!Javaでのオートボクシングと呼ばれるもののために、オブジェクトのように動作するだけです
Christoph

11
しかし、文字列にはメソッドとプロパティがあり、Stringのプロトタイプは間違いなく変更できます。彼らは確かにオブジェクトのように振る舞います。
キャメロンマーティン

9
@ddlshack:Christophが説明したように、これはオートボクシングが原因です。あなたが経由で文字列を定義した場合var foo = new String('foo');、その後、それは文字列オブジェクトになります(そしてtypeofこれを確認します)。しかし、文字列リテラルを介して宣言する場合、それらは文字列プリミティブです。参照:developer.mozilla.org/en/JavaScript/Reference/Global_Objects/...
Lèseはmajesté

26

あなたの質問はすでにSqueegyによって満足に答えられています-それはオブジェクト対プリミティブとは何の関係もありませんが、同じ参照オブジェクト内の変数と設定プロパティの再割り当てに関するものです。

回答とコメントのJavaScriptの型については多くの混乱があるようですので、JavaScriptの型システムの簡単な紹介を次に示します。

JavaScriptには、基本的に異なる2種類の値があります。プリミティブとオブジェクトです(「ハッシュ」のようなものはありません)。

文字列、数値、ブール値、nullおよびundefinedプリミティブであるオブジェクトは、プロパティを持つことができるすべてのものです。配列や関数も通常のオブジェクトであるため、任意のプロパティを保持できます。これらは内部の[[Class]]プロパティが異なるだけです(関数には[[Call]]と[[Construct]]と呼ばれるプロパティがありますが、それは詳細です)。

プリミティブ値がオブジェクトのように動作する理由は、オートボクシングのためですが、プリミティブ自体はプロパティを保持できません。

次に例を示します。

var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);

これは出力しますundefinedaプロパティを割り当てるときにオブジェクトに昇格されるプリミティブ値を保持しますfoo。ただし、この新しいオブジェクトはすぐに破棄されるため、の値fooは失われます。

次のように考えてください。

var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created

Mozilla Foundationのページ「JavaScriptの再紹介(JSチュートリアル)」では、JavaScriptオブジェクトを「名前と値のペアの単純なコレクションとして記述しています。したがって、それらは...に似ています」と説明されており、その後に辞書のリストが続きます。 、ハッシュテーブル、およびさまざまなプログラミング言語のハッシュマップ。同じページでは、オブジェクトプロパティ参照をハッシュテーブルルックアップとして説明しています。したがって、オブジェクトはすべて「ハッシュ」テーブルのようなものです。これは他の有用な情報を無効にしませんが、Chris Lloydの元の特性は不正確ではありませんでした。
Cパーキンス

2

あなたが多かれ少なかれ正しいのは、「ハッシュ」と呼んでいるものは、実際にはオブジェクトの省略構文にすぎないということです。

最初の例でabの両方が同じオブジェクトを参照しています。2番目の例では、他のものを参照するようにaを変更ます。


では、なぜObjectのダブルスタンダードなのでしょうか。
Chris Lloyd、

それは二重の標準ではありません。最初の例では、aとbは同じオブジェクトを参照していますが、そのオブジェクトのプロパティを変更しているだけです。2番目の例では、別のオブジェクトを指しています。
ケビン

1
いいえ、違いは、2番目のケースでは、オブジェクトではなく文字列を扱うということです。
Nosredna 2009

1
明確にするために:これは、文字列自体のコピーを返す文字列とは関係ありません。2つのコードスニペットが異なる理由は、Kevinの2番目の段落にあります(Squeegyの回答でさらに詳しく説明されています)。
チャック、

変数に文字列またはオブジェクトがあるかどうかは関係ありません。新しい別の値を割り当てると、変数にはその新しい別の値が含まれます。
STH

2

ここに私のバージョンの答えがあります:

obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"

// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"

// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory

0

新しい文字列オブジェクトを指すようにaを設定している一方で、bは古い文字列オブジェクトを指し続けています。


0

最初のケースでは、変数に含まれるオブジェクトのいくつかのプロパティを変更し、2番目のケースでは、変数に新しい値を割り当てます。それは根本的に異なるものです。変数ab最初の割り当てによって魔法のようにリンクされているわけではなく、同じオブジェクトが含まれているだけです。b変数に新しい値を割り当てるまでは、2番目の例でも同じです。


0

違いは、単純型とオブジェクトの間です。

オブジェクトであるもの(配列や関数など)は、参照渡しされます。

単純なタイプ(文字列や数値など)はすべてコピーされます。

私は常に便利なcopyArray関数を持っているので、同じ配列に大量のエイリアスを作成していないことを確認できます。


違いは多くのシナリオで顕著ではありませんが、JavaScriptは実際には参照によって渡されたり割り当てられたりしません。参考値をコピーします。
ファンパブロカリファノ

私はちょうどリンクを貼り付けますので、これらの人はすでに、それを説明する良い仕事をした:stackoverflow.com/questions/40480/is-java-pass-by-referenceは(私は、Javaを意味するが、渡して割り当てるためのセマンティクス値/参照はJavascriptと同じです)
フアンパブロカリファノ

1
実際、この答えは正しくありません。すべてがJavaScriptの値によって渡されます。MDNから、「関数呼び出しのパラメーターは関数の引数です。引数は値によって関数に渡されます。関数が引数の値を変更した場合、この変更はグローバルにも呼び出し元の関数にも反映されません。ただし、オブジェクト参照は値も特殊であり、関数が参照されたオブジェクトのプロパティを変更した場合、その変更は関数の外に表示されます。」developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
bittersweetryan

プリミティブは不変オブジェクトのように動作します(厳密モードのプリミティブとまったく同じです)。この答えは正しくありません。
Ry-
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.