为什么array.forEach(()=> {array.pop()})不会清空数组

时间:2019-01-07 21:07:45

标签: javascript arrays node.js

在nodejs REPL上时,我试图清理定义为const array = [...]的数组,但发现使用array.forEach(() => /pop|shift/())不起作用。这样的表达式之后,数组仍将保留值。

我很清楚清理数组的更好方法,例如array.splice(0),但我对这种行为感到很好奇,至少对我来说是违反直觉的。

这是测试:

const a = [1, 2, 3]

a.forEach(() => {
  a.shift()
})

console.log(a) // [ 3 ]

const b = [1, 2, 3]

b.forEach(() => {
  b.pop()
})

console.log(b) // prints [ 1 ]


注释

  1. 起初我使用的是arr.forEach(() => arr.pop()),所以我虽然其中一个值使forEach短路,但是将lambda包裹在主体块{ .. }中也会产生相同的结果。

  2. 结果在不同的节点版本和浏览器上都是一致的..因此,这似乎是行为明确的行为。

  3. 剩余值的数量(仍留在结果数组中)根据输入数组的长度而变化,似乎为Math.floor(array.length / 2)

  4. 剩余值始终根据所使用的/pop|shift/方法进行排序,因此某些调用实际上正在更改输入数组。

  5. 它还通过调用Array.prototype.forEach(array, fn)

  6. 返回相同的结果

3 个答案:

答案 0 :(得分:3)

在迭代数组时修改数组通常是一个坏主意。实际上,在Java中,尝试这样做会导致引发异常。但是让我们将forEach转换成老式的for循环,也许您会发现问题所在。

for (let i = 0; i < a.length; ++i) {
    a.pop();
}

现在更清楚了吗?每次迭代时,将最后一个元素弹出时,都会将数组的长度缩短1。因此,循环将在迭代一半以上的元素之后结束-因为到那时,它将也删除一半的元素,从而导致i的值大于数组的当前长度。

使用forEach时会发生同样的事情:弹出时,每次迭代都会缩短数组,导致循环仅在迭代了一半元素后终止。换句话说,随着数组缩小,迭代器变量将向前移动超过数组的末端。

答案 1 :(得分:1)

.pop

让我们这样做:

let arr = 'abcde'.split('');
arr.forEach((x,i,a) => {
    console.log('before', x,i,a);
    arr.pop();
    console.log('after', x,i,a);
});
console.log(arr);

您的索引在增加,但是长度在减少,因此,当索引在第一个元素中时,您将删除最后一个元素,因此删除数组的右半部分将是结果。

.shift

相同:迭代索引以一种方式进行,长度以另一种方式进行,因此整个过程在工作中期停止:

let arr = 'abcde'.split('');
arr.forEach((x,i,a) => {
    console.log('before', x,i,a);
    arr.shift();
    console.log('after', x,i,a);
});
console.log(arr);

答案 2 :(得分:1)

从此处查看此引用:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

  

如果更改了数组中现有元素的值,则   传递给回调的值将是forEach()时的值   拜访他们;被访问之前删除的元素不会   参观过。

您要从头开始迭代,并在每次迭代中删除最后一个元素。这意味着您将每次迭代前进1,并将长度减少1。因此,为什么要结束floor(initialLength / 2)迭代。您正在修改要forEach处理的同一数组,如上所述,这意味着您不会为这些pop元素调用回调。