在将Array.reduce应用于数组时更改数组的后果是什么 [英] What are the consequences of mutating the array while applying Array.reduce to it
问题描述
假设我有一个数组:
const ar = [1,2,3,4];
然后对它应用reduce函数,并在该函数中删除如下元素:
And I apply reduce function to it and inside that function I remove elements like this:
ar.reduce((result, element, index, original)=>{
original.pop();
}, []);
对于前两个元素,该函数将仅执行两次。这是可以理解的,因为我在之前的调用中删除了第3和第4个元素。
The function will be executed only two times for the first two elements. That's understandable since I've removed 3rd and 4th elements during previous calls.
但是有趣的是,如果我执行该函数并删除当前元素:
But what is interesting is that if I execute the function and remove the current element:
ar.reduce((result, element, index, original)=>{
original.splice(index, 1);
}, []);
该函数对于前两个元素仍然执行两次。为什么 3rd
和 4th
元素不执行,因为它们仍保留在数组中?
the function is still executed two times for the first two elements. Why is it not executed for the 3rd
and 4th
elements since they remain in the array?
此行为是否记录在任何地方?在规格上?
Is this behavior documented anywhere? In a spec maybe?
推荐答案
在第二个示例中,实际上是对 1st 和 3rd执行的元素,而不是前两个元素:
In your second example, it is in fact executed for the 1st and 3rd elements, not for the first two:
const ar = [1, 2, 3, 4];
ar.reduce((result, element, index, original)=>{
console.log(element, index);
original.splice(index, 1);
}, []);
console.log(ar);
1 2 3 4
^
在这里,而reduce的 element
为 1
,索引
为 0
,它调用 splice
,删除第一个元素,然后迭代到下一个索引:
Here, while reduce's element
is 1
and index
is 0
, it calls splice
, removing the first element, then iterates to the next index:
2 3 4
^
这里,reduce的 element
是 3
和 index
是 1
。删除之后, index
将等于 ar.length
并且停止,剩下
Here, reduce's element
is 3
and index
is 1
. After removing that, index
will be equal to ar.length
and it stops, leaving you with
2 4
reduceRight()
仍将访问所有元素的原因是因为您向后迭代,并且先前元素的位置不受在当前索引处拼接元素的影响:
The reason reduceRight()
will still visit all the elements is because you iterate backwards, and the previous element positions are not affected by splicing the element at the current index:
const ar = [1, 2, 3, 4];
ar.reduceRight((result, element, index, original)=>{
console.log(element, index);
original.splice(index, 1);
}, []);
console.log(ar);
以及演练:
element = 4, index = 3
1 2 3 4
^
element = 3, index = 2
1 2 3
^
element = 2, index = 1
1 2
^
element = 1, index = 0
1
^
要回答您的问题,是作为规范的一部分,ECMAScript为 Array#reduce()
记录了此行为:
To answer your question, yes ECMAScript documents this behavior for Array#reduce()
as part of the specification:
reduce
处理的元素范围在第一次调用callbackfn <之前设置/ code>。
reduce
开始调用后追加到数组的元素将不会被callbackfn
访问。如果更改了数组的现有元素,则传递给callbackfn
的值将是reduce访问它们时的值;在调用reduce
之后并且被访问之前删除的元素不会被访问。
The range of elements processed by
reduce
is set before the first call tocallbackfn
. Elements that are appended to the array after the call toreduce
begins will not be visited bycallbackfn
. If existing elements of the array are changed, their value as passed tocallbackfn
will be the value at the time reduce visits them; elements that are deleted after the call toreduce
begins and before being visited are not visited.
与上述完全相同的段落也适用于 reduceRight
。
And the exact same paragraph as above applies to reduceRight
as well.
以下是用于 Array#reduce()
,遵循规范中的步骤:
Below is a polyfill for Array#reduce()
, following the steps from the specification:
Object.defineProperty(Array.prototype, 'reduce', {
configurable: true,
writable: true,
value: Array.prototype.reduce || function reduce(callbackfn) {
"use strict";
// 1.
if (this === undefined || this === null) {
throw new TypeError("Array.prototype.reduce called on null or undefined");
}
let O = Object(this);
// 2.
let len = ToLength(O.length);
// 3.
if (typeof callbackfn != 'function') {
throw new TypeError(`${String(callbackfn)} is not a function`);
}
// 4.
if (len == 0 && arguments.length < 2) {
throw new TypeError("Reduce of empty array with no initial value");
}
// 5.
let k = 0;
let accumulator;
// 6.
if (arguments.length >= 2) {
// a.
accumulator = arguments[1];
// 7.
} else {
// a.
let kPresent = false;
// b.
while (!kPresent && k < len) {
// i.
let Pk = String(k);
// ii.
kPresent = Pk in O;
// iii.
if (kPresent) accumulator = O[Pk]; // 1.
// iv.
k++;
}
// c.
if (!kPresent) throw new TypeError("Reduce of empty array with no initial value");
}
// 8.
while (k < len) {
// a.
let Pk = String(k);
// b.
let kPresent = Pk in O;
// c.
if (kPresent) {
// i.
let kValue = O[Pk];
// ii.
accumulator = callbackfn(accumulator, kValue, k, O);
}
// d.
k++;
}
// 9.
return accumulator;
}
});
function ToInteger(argument) {
let number = Number(argument);
if (isNaN(number)) return 0;
switch (number) {
case 0:
case Infinity:
case -Infinity:
return number;
}
return parseInt(number);
}
function ToLength(argument) {
let len = ToInteger(argument);
if (len <= 0) return 0;
if (len == Infinity) return Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1;
return len;
}
这篇关于在将Array.reduce应用于数组时更改数组的后果是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!