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

我在Github前端面试题集合中遇到过这个问题:

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

问题:foo.x的价值是什么?

答案是undefined

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

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

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

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

确定foo.x引用{n: 1}对象的属性x ,将{n: 2}分配给foo ,并将foo{n: 2}的新值赋给{n: 1}对象。

重要的是, foo.x引用的foo是在foo更改之前确定的。

请参阅ES5规范的第11.13.1节 :

  1. lref是评估LeftHandSideExpression的结果。

  2. 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指的是在执行开始之前哪个对象。

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

回到我们留下的问题:

foo.x = foo;

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

所以在执行这个语句之后{n:1}将会变成{n:1,x:{n:2}} ,而bar仍然指向它。 foo现在指的是{n:2}

所以执行foo.x给出了未定义,因为foo中只有一个值是{n:2}。

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

对象{n:1,x:Object}

我想我会添加另一个,我发现是有用的方式来思考这个。

那些最后的variables赋值相当于写入bar.x = foo = {n:2}; ,因为这些variables只是在内存中引用相同的东西。

换句话说, foobar起初都是引用同一个对象{n:1} 。 当您使用foo.x = ,您正在访问{n:1}并向其中添加x属性。 这可以用barfoo来完成,因为它们都指向内存中的同一个对象! 没什么区别。

然后,当你完成这一行, foo.x = foo = {n:2} ,你将通过对象字面值语法在内存中创build另一个全新的对象,并将foo指向对象{n:2}现在是{n:1, x: {n: 2} 。 不过,这并不影响foo在将x属性添加到它时指向的内容。

这是相当混乱的,但我认为你有必要考虑一下这样一个事实,即variables只是指向内存中的地方/对象的指针,并且对象文本语法不会改变以前存在的对象(即使它们看起来相似)。 这是创造一个全新的。

对这个问题的接受答案的开始可能也是有帮助的。

对象variables只是对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: {n: 2}}修改的对象将通过参考bar访问。 如果没有参考栏,则原始对象将被销毁