问题描述
JavaScript会在什么时候确定任务的左侧-是在评估右侧之前还是之后?
At what point does JavaScript determine the left-hand side of an assignment — is it before or after the right-hand side is evaluated?
例如,这段代码是做什么的?
For example, what does this code do?
var arr = [{thing:1},{thing:2},{thing:3},{last:true}];
arr[arr.length - 1].newField = arr.pop();
推荐答案
赋值运算符的左侧首先被评估.
The left-hand side of an assignment operator is evaluated first.
ES2015以来的规范可以在"赋值运算符部分的"运行时语义:评估部分,可以粗略地概括为:
The specification for this as of ES2015 can be found in the "Runtime Semantics: Evaluation" portion of the "Assignment Operators" section and can be very roughly summarized as:
- 评估LHS以确定参考
- 评估RHS以确定价值
- 将值分配给引用
关于数组.pop()
的示例,它看起来好像会将原始的最后一个项目分配给结果的最后一个项目中的字段,但这只会在首先评估RHS而不是LHS的情况下发生
With regards to the array .pop()
example, it might have looked like it would assign the originally last item to a field in the resulting last item — but that would only happen if the RHS were evaluated first, rather than the LHS.
实际发生的情况是,左侧首先产生对原始最后一个对象{last:true}
的引用.此后,array.pop()
从数组末尾删除相同对象后,将返回该对象.然后发生分配,因此对象最终看起来像obj = {last:true, newField:obj}
.由于在原始示例中没有保留对obj
的引用,
What actually happens is that the left-hand side first yields a reference to the original last object {last:true}
. After this, the array.pop()
returns the same object after removing it from the end of the array. Then the assignment happens, so that the object ends up looking like obj = {last:true, newField:obj}
. Since no reference to obj
was kept in the original example,
改为将分配扩展到代码中,并添加几个额外的变量,以便我们可以检查行为,如下所示:
Expanding the assignment out to code instead, and adding a couple extra variables so we can check the behavior, might look something like this:
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.")
这篇关于分配时的JavaScript评估顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!