JavaScript 代码技巧:foo.x 的值是什么

IT技术 javascript object variable-assignment
2021-01-31 14:47:56

我在GitHub前端面试题合集中发现了这个问题:

var foo = {n: 1};
var bar = foo;
foo.x = foo = {n: 2};

问题:foo.x 的值是多少?

答案是undefined

我做了一些研究,我明白这个问题是(如果我错了,请纠正我):

  • var foo = {n: 1};声明一个foo属性n等于 1的对象
  • var bar = foo;声明一个对象bar该对象引用与 相同的对象foo
  • foo.x = foo = {n: 2}; 我相信这等于 foo.x = (foo = {n: 2});
  • 然后我得到了foo.x等于undefined. 但是, 的值bar.x是 object {n:2}

如果barfoo引用同一个对象,为什么bar.xfoo.xis得到一个值undefined到底发生了foo.x = foo = {n: 2};什么?

6个回答
foo.x = foo = {n: 2};

判定foo.x是指一种属性x的的{n: 1}对象,受让人{n: 2}foo,和受让人的新值foo- {n: 2}-为属性x的的{n: 1}对象。

重要的是,foofoo.x指的是之前确定foo的变化。

请参阅ES5 规范的第 11.13.1 节

  1. lref是评估LeftHandSideExpression的结果

  2. rref为对AssignmentExpression求值的结果

赋值运算符从右到左关联,所以你得到:

foo.x = (foo = {n: 2})

左侧先于右侧进行评估。

@soulcheck:这是标准规定的。现在找到重要的部分。(这也是常识所要求的 – 考虑一下(x = {}).y = x
2021-03-15 14:47:56
参考解析顺序是由某个标准强制执行的,还是它的具体实现方式?
2021-03-25 14:47:56
@minitech 好的,我明白了。谢谢。
2021-04-01 14:47:56
@soulcheck 谢谢。所以你的意思foo{n:2}现在在赋值之后,但foo.x属于旧的 'foo = {n:1}' 对象,该对象不再存在,因为foo已更改为{n:2}And 因此foo.x无法分配值但bar.x得到值,因为bar总是存在并且从未改变?天啊...
2021-04-03 14:47:56
@AndyHu:foo.xx的是旧的foobar也指旧的foox不存在于新的foo.
2021-04-09 14:47:56
foo.x = foo = {n: 2};

这里foo指的是{n:1}赋值之前的对象,即语句执行之前。

该语句可以重写为 foo.x = (foo = {n:2});

在对象方面,上述语句可以重写为 {n:1}.x = ( {n:1} = {n:2} );

由于分配仅从右到左发生。所以在这里我们只需要foo在执行开始之前保持一个引用哪个对象的检查

关于解决RHS: foo = {n:2};现在foo指的是{n:2};

回到我们留下的问题:

foo.x = foo;

现在foo.x在 LHS 上仍然是,{n:1}.xfoo在 RHS 上是{n:2}

所以在这条语句被执行之后{n:1}会变成{ n:1, x:{n:2} }with bar 仍然引用它。作为在哪里foo现在将参照{n:2}

所以在执行时foo.x给出,undefined因为只有 1 个值foo{n:2}

但是如果你尝试执行bar.x它会给{n:2}. 或者如果你只是执行bar结果将是

Object {n: 1, x: Object}

据我了解表达:

foo.x = foo = {n: 2};

完全一样:

foo.x = {n: 2} ; 
foo = {n: 2};

在此之后,很明显:

 bar=={n: 1, x: {n:2}};
 foo=={n:2};
 foo.x==undefined

我想我会添加另一种,我发现的,有用的思考方式。

那些最后的变量赋值相当于 write bar.x = foo = {n:2};,因为这些变量只是对内存中相同事物的引用。

换句话说,foobar首先,两者都引用同一个对象,{n:1}当您使用 时foo.x =,您正在访问{n:1}x属性并将其添加到其中。这可以通过bar或者foo因为它们都指向内存中的同一个对象!没有什么不同的。

然后,当您完成该行时,foo.x = foo = {n:2}您将通过对象字面量语法和设置指向对象,而不是现在的,在内存中创建另一个全新的对象但是,这不会影响您向其添加属性时指向的内容。foo{n:2}{n:1, x: {n: 2}foox

这很令人困惑,但我认为您考虑这样一个事实是有道理的:变量只是指向内存中位置/对象的指针,并且对象文字语法不会改变以前存在的对象(即使它们看起来很相似)。它正在创造一个全新的。

这个问题的公认答案的开头也可能会有所帮助。

这很有帮助!对我来说,关键是意识到bar变成{n: 1, x: {n: 2}}foo变成{n: 2}这就是问题引入bar变量的原因。
2021-04-05 14:47:56

需要理解的是,对象变量只是对 JavaScript 中对象的引用,而不是对象本身。

var foo = {n: 1}-> foo 指向真实对象 {n: 1} var bar = foo-> bar 现在也是真实对象 {n: 1} 的引用

棘手的部分当然是第三行: 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如果没有引用栏,原始对象将被销毁