分配时的 JavaScript 评估顺序

Posted

技术标签:

【中文标题】分配时的 JavaScript 评估顺序【英文标题】:JavaScript evaluation order when assigning 【发布时间】:2017-12-28 01:48:04 【问题描述】:

javascript 在什么时候确定赋值的左侧——是在评估右侧之前还是之后?

例如,这段代码是做什么的?

var arr = [thing:1,thing:2,thing:3,last:true];
arr[arr.length - 1].newField = arr.pop();

【问题讨论】:

【参考方案1】:

首先计算赋值运算符的左侧。

ES2015 的规范可以在"Runtime Semantics: Evaluation" portion of the "Assignment Operators" section 中找到,可以非常粗略地总结为:

    评估 LHS 以确定参考 评估 RHS 以确定值 将值分配给引用

关于数组.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.")

【讨论】:

【参考方案2】:

监控此分配期间发生的事情的一个好方法是在数组上定义一个代理:

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();

现在控制台将显示何时访问数组属性(包括数字索引、lengthpop 方法)以及设置它们的时间。

前两行说明首先计算左侧:

获取长度 得到 3

其他行在pop的执行过程中产生:

越来越流行 长度 获得 3 将长度设置为 3

【讨论】:

以上是关于分配时的 JavaScript 评估顺序的主要内容,如果未能解决你的问题,请参考以下文章

Java中评估顺序的规则是啥?

左侧分配是否无效,编译时会出现编译错误或评估错误

POD安全策略评估顺序(多个角色)

在这种方法调用和传入参数的情况下,Java 评估顺序是不是得到保证

为啥具有短路操作的并行 Java Stream 会评估 Stream 的所有元素,而顺序 Stream 不会?

[Java]程序运行时的内存分配