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}
。
如果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}
对象。
重要的是, foo.x
引用的foo
是在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指的是在执行开始之前哪个对象。
解决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只是在内存中引用相同的东西。
换句话说, foo
和bar
起初都是引用同一个对象{n:1}
。 当您使用foo.x =
,您正在访问{n:1}
并向其中添加x
属性。 这可以用bar
或foo
来完成,因为它们都指向内存中的同一个对象! 没什么区别。
然后,当你完成这一行, 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
访问。 如果没有参考栏,则原始对象将被销毁