JavaScript在什么时候确定作业的左侧 - 是在评估右侧之前还是之后?
例如,这段代码做了什么?
var arr = [{thing:1},{thing:2},{thing:3},{last:true}];
arr[arr.length - 1].newField = arr.pop();
答案 0 :(得分:2)
首先评估赋值运算符的左侧。
ES2015的规范可以在"Runtime Semantics: Evaluation" portion of the "Assignment Operators" section中找到,可以大致概括为:
关于数组.pop()
示例,它看起来可能会将最初的最后一项分配给最后一项中的字段 - 但这只会在首先评估RHS时发生,而不是LHS。
实际发生的是左侧首先产生对原始最后一个对象{last:true}
的引用。在此之后,array.pop()
在从数组末尾删除它后返回相同的对象。然后进行赋值,使对象最终看起来像obj =
{last:true, newField:obj}
。由于原始示例中未保留对obj
的引用,
将分配扩展到代码,并添加一些额外的变量以便我们检查行为,可能看起来像这样:
function pseudoAssignment() {
// left-hand side evaluated first
var lhsObj = arr[arr.length - 1];
var lhsKey = 'newField';
// then the right-hand side
var rhsVal = arr.pop();
// then the value from RHS is assigned to what the LHS references
lhsObj[lhsKey] = rhsVal;
// `(a = b)` has a value just like `(a + b)` would
return rhsVal;
}
var arr = [{thing:1},{thing:2},{thing:3},{last:true}];
_lastObj = arr[arr.length - 1];
// `arr[arr.length - 1].newField = arr.pop();`
_result = pseudoAssignment();
console.assert(_lastObj.newField === _lastObj,
"The last object now contains a field recursively referencing itself.")
console.assert(_result === _lastObj,
"The assignment's result was the object that got popped.")
console.assert(arr.indexOf(_lastObj) === -1,
"The popped object is no longer in the array.")
答案 1 :(得分:0)
监视此分配期间发生的事情的一种很好的方法是在阵列上定义代理:
var arr = [{thing:1},{thing:2},{thing:3},{last:true}];
arr = new Proxy(arr, {
get: function (obj, key) {
console.log('getting ', key);
return obj[key];
},
set: function (obj, key, value) {
console.log('setting ', key, ' to ', value);
return obj[key] = value;
}
});
arr[arr.length - 1].newField = arr.pop();
现在控制台将显示何时访问数组属性(包括数字索引,length
和pop
方法)以及何时设置它们。
前两行说明首先评估左侧:
得到长度
得到3
在执行pop
期间生成其他行:
流行 得到长度
得到3
将长度设置为3