在将Array.reduce应用于数组时更改数组的后果是什么 [英] What are the consequences of mutating the array while applying Array.reduce to it

查看:74
本文介绍了在将Array.reduce应用于数组时更改数组的后果是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个数组:

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 to callbackfn. Elements that are appended to the array after the call to reduce begins will not be visited by callbackfn. If existing elements of the array are changed, their value as passed to callbackfn will be the value at the time reduce visits them; elements that are deleted after the call to reduce 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆