首页 > 解决方案 > 在 reducer 函数中更改 targetArray.length 时的 targetArray.reduce 行为

问题描述

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

规范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在每次迭代时故意更新,而不是像规范中指定的那样从一开始就保持固定?

标签: javascriptarraysreduceecmascript-5

解决方案


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


推荐阅读