targetArray.reduce 在 reducer 函数中更改 targetArray.length 时的行为

targetArray.reduce behavior when targetArray.length is changed in the reducer function

这个问题主要针对那些使用 ECMAScript 规范的人 and/or 实施它。

spec 指定了 Array.prototype.reduce(callbackfn[,initialValue]) 的算法。 看到算法,第 2 步,读取 Let len be ? ToLength(? Get(O, "length")) 之后,我的理解是 len 再也不会更新了。但是,看看这个:

const a = [0,1,2,3,4];
a.reduce(p=>{
  a.splice(0,1);
  console.log(a);
  return p;
},a);
// Logs:
// [1,2,3,4]
// [2,3,4]
// [3,4]
// and that's it

由于迭代应该 运行 while k<lenk 是迭代计数器),这种行为表明,在我尝试的实现中(Chrome, Edge, Node), len 在每次迭代中被更新为当前迭代的 a 的长度。

这真的是预期的行为吗? len 有意在每次迭代时更新,而不是像规范中指定的那样从一开始就保持固定,这是否有具体原因?

您看到的行为是因为步骤 9.2:仅当 属性 出现在对象上时才会调用回调。它确实迭代了5次,但是在你删除了3个元素之后,在第四次和第五次迭代中a.hasOwnProperty(3)a.hasOwnProperty(4)都是假的。

我们可以用代理来最好地证明这一点:

const a = [0,1,2,3,4];
new Proxy(a, {
 has(target, p) { const v = Reflect.has(target, p); console.log("has", p, v); return v },
 get(target, p) { const v = Reflect.get(target, p); console.log("get", p, v); return v }
}).reduce((p, e) => {
  a.length--;
  console.log("at " + e, a);
  return p;
}, null);

或者通过在 .length 更改不影响属性的对象上调用 reduce

const a ={0:0,1:1,2:2,3:3,4:4,length:5,reduce:Array.prototype.reduce};
a.reduce((p, e) => {
  a.length--;
  console.log("at " + e, a);
  return p;
}, null);