この問題は、GitHubのフロントエンドインタビューの質問集で発見しました。
_var foo = {n: 1}; var bar = foo; foo.x = foo = {n: 2};
_質問:foo.xの値は何ですか?
答えはundefined
です。
私はいくつかの研究を行ってきましたが、この問題が理解しているのは間違っています(間違っている場合は修正してください):
var foo = {n: 1};
_は、プロパティfoo
が1に等しいオブジェクトn
を宣言します.var bar = foo;
_は、bar
と同じオブジェクトを参照するオブジェクトfoo
を宣言します。foo.x = foo = {n: 2};
_は、foo.x = (foo = {n: 2});
と等しいと思いますundefined
に等しい_foo.x
_を取得しました。ただし、_bar.x
_の値はオブジェクト_{n:2}
_です。bar
とfoo
が同じオブジェクトを参照する場合、なぜ_bar.x
_が値を取得したのに_foo.x
_がundefined
であるのですか? _foo.x = foo = {n: 2};
_で実際に何が起こっていますか?
foo.x = foo = {n: 2};
foo.x
が{n: 1}
オブジェクトのプロパティx
を参照し、{n: 2}
をfoo
に割り当て、foo
– {n: 2}
– {n: 1}
オブジェクトのプロパティx
へ。
重要なことは、foo
が変更される前に、foo.x
が参照するfoo
が決定されることです。
ES5仕様のセクション11.13.1 を参照してください:
lrefをLeftHandSideExpressionの評価結果とします。
rrefをAssignmentExpressionの評価結果とします。
代入演算子は右から左に関連付けられるため、次のようになります。
foo.x = (foo = {n: 2})
左側が右側の前に評価されます。
foo.x = foo = {n:2};
ここで、fooは{n:1}代入前、つまりステートメントが実行される前のオブジェクトを指します。
ステートメントはfoo.x =(foo = {n:2});のように書き換えることができます
オブジェクトの用語では、上記のステートメントは{n:1} .x =({n:1} = {n:2});のように書き直すことができます
割り当ては右から左にのみ行われるため。したがって、ここでは、fooが実行を開始する前にどのオブジェクトを参照しているかを確認する必要があります。
R.H.Sの解法:foo = {n:2}; fooは{n:2}を参照しています。
残っている問題に戻って、
foo.x = foo;
L.H.Sのfoo.xはまだ{n:1} .xであるのに対し、R.H.Sのfoo.xは{n:2}です。
そのため、このステートメントが実行された後、{n:1}は{n:1、x:{n:2}}になりますが、barはまだそれを参照しています。 fooが{n:2}を指すようになりました。
Fooには{n:2}の値が1つしかないため、実行時にfoo.xはundefinedを返します。
しかし、bar.xを実行しようとすると、{n:2}が得られます。または、単にbarを実行すると、結果は
オブジェクト{n:1、x:Object}
私は別の、私が見つけたもの、これについての便利な考え方を追加すると思いました。
これらの変数はメモリ内の同じものへの単なる参照であるため、これらの最後の変数の割り当てはbar.x = foo = {n:2};
の記述と同等です。
つまり、foo
とbar
は、最初は両方とも同じオブジェクト{n:1}
を参照しています。 foo.x =
を使用する場合、{n:1}
にアクセスし、x
プロパティを追加します。これは、bar
またはfoo
のいずれかで実行できます。これらは両方ともメモリ内の同じオブジェクトを指しているからです!違いはありません。
次に、その行foo.x = foo = {n:2}
を完了すると、オブジェクトリテラル構文を使用してメモリ内に別のbrandオブジェクトを作成し、foo
をポイントに設定します。 tothatオブジェクト、{n:2}
、現在の{n:1, x: {n: 2}
の代わりに。ただし、これはfoo
プロパティを追加したときにx
が指すものには影響しません。
これはかなり紛らわしいですが、変数はメモリ内の場所/オブジェクトへの単なるポインターであり、オブジェクトリテラル構文は以前の既存のオブジェクトを変更していないという事実について考えるのは理にかなっていると思います(たとえ似ていても)。新しいものを作成しています。
この質問 への受け入れられた回答の始まりも役立つかもしれません。
オブジェクト変数はJavaScript内のオブジェクトへの単なる参照であり、オブジェクト自体ではないことを理解してください。
var foo = {n: 1}
-> fooは実オブジェクト{n:1}を参照しますvar bar = foo
-> barは実オブジェクト{n:1}への参照にもなりました
トリッキーな部分はもちろん3行目です:foo.x = foo = {n: 2}
これは次と同等です:(reference to {n: 1}).x = (foo = {n: 2})
->この行が完全に評価された後、fooは新しいオブジェクト{n:2}への参照になります。ただし、fooは行の評価前に元のオブジェクト{n: 1}
を参照するため、行が評価された後、元のオブジェクト{n: 1}
は{n: 1, x: [reference to]{n: 2}}
になり、変更されたオブジェクトは参照bar
。参照バーがない場合、元のオブジェクトは破棄されます
私が表現を理解するように:
foo.x = foo = {n: 2};
次とまったく同じです。
foo.x = {n: 2} ;
foo = {n: 2};
そしてこの後、次のことが明らかになりました:
bar=={n: 1, x: {n:2}};
foo=={n:2};
foo.x==undefined