【问题标题】:Why array.forEach(() => { array.pop() }) would not empty the array为什么 array.forEach(() => { array.pop() }) 不会清空数组
【发布时间】:2019-06-02 13:30:51
【问题描述】:

在 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 ]

注意事项

    1234563
  1. 结果在不同的节点版本和浏览器中是一致的......所以它看起来像是定义良好的行为。

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

  3. 剩余的值总是根据所使用的/pop|shift/ 方法排序,因此某些调用实际上是在更改输入数组。

  4. 调用Array.prototype.forEach(array, fn)也返回相同的结果

【问题讨论】:

  • 您从头开始迭代,每次迭代都删除最后一个元素。这意味着您正在向前移动 1,并且每次迭代将长度减少 1。因此,为什么您最终会使用 floor(initialLength / 2) 迭代。
  • 我在 Chrome 71 上的输出有点不同:[5, 6, 7] 用于第一个日志,[1, 2, 3] 用于第二个
  • @MatthewHerbst 我修复了输入数组!

标签: javascript arrays node.js


【解决方案1】:

从这里查看此报价:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

如果数组中现有元素的值发生变化,则 传递给回调的值将是当时的值 forEach() 拜访他们;在被访问之前被删除的元素不是 访问过。

您从头开始迭代,每次迭代都删除最后一个元素。这意味着您正在向前移动 1,并且每次迭代将长度减少 1。因此,为什么您最终会使用 floor(initialLength / 2) 迭代。您正在修改与 forEaching 相同的数组,如上所述,这意味着您不会为那些 pop'd 元素调用回调。

【讨论】:

  • 我明白你在说什么!知道为什么这不会陷入无限循环吗? ` a = [ 1 ]; a.forEach((_, i) => { a.push(i) })` .. 好像forEach 保留了一些初始数组值的副本
  • 是的,看看我在引用 @eridal 上方包含的链接
【解决方案2】:

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

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

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

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

【讨论】:

  • 这对我有帮助。谢谢
【解决方案3】:

.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);

【讨论】:

    猜你喜欢
    • 2014-05-26
    • 2013-10-14
    • 2023-03-08
    • 2017-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-30
    相关资源
    最近更新 更多