This questions is primarily for those who work with the ECMAScript spec and/or implement it.
The spec specifies the algorithm for Array.prototype.reduce(callbackfn[,initialValue])
.
See that the algorithm, step 2, reads Let len be ? ToLength(? Get(O, "length"))
and after that, my understanding is that len
is never updated again. However, take a look a this:
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
Since the iterations are supposed to run while k<len
(k
being the iteration counter), this behavior suggests that, in the implementations I tried (Chrome, Edge, Node), len
is being updated at each iteration to lenght of a
of the current iteration.
Is that really the intended behavior? Is there a concrete reason why len
is purposely updated at each iteration instead of keeping it fixed from the start like specified in the spec?
The behaviour you are seeing is because of step 9.2: the callback is only called when the property is present on the object. It does iterate 5 times, but after you deleted 3 elements, in the fourth and fifth iteration a.hasOwnProperty(3)
and a.hasOwnProperty(4)
are false.
We can best demonstrate this with a proxy:
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);
Or alternatively by calling reduce
on an object whose .length
changes don't affect the properties:
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);