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

时间:2019-07-09 19:12:02

标签: javascript arrays reduce ecmascript-5

这个问题主要针对那些使用ECMAScript规范和/或实现它的人。

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会在每次迭代中有目的地进行更新,而不是像规范中指定的那样从一开始就将其固定?

1 个答案:

答案 0 :(得分:3)

您看到的行为是由于步骤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);

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

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