在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 ]
起初我使用的是arr.forEach(() => arr.pop())
,所以我虽然其中一个值使forEach
短路,但是将lambda包裹在主体块{ .. }
中也会产生相同的结果。
结果在不同的节点版本和浏览器上都是一致的..因此,这似乎是行为明确的行为。
剩余值的数量(仍留在结果数组中)根据输入数组的长度而变化,似乎为Math.floor(array.length / 2)
剩余值始终根据所使用的/pop|shift/
方法进行排序,因此某些调用实际上正在更改输入数组。
它还通过调用Array.prototype.forEach(array, fn)
答案 0 :(得分:3)
在迭代数组时修改数组通常是一个坏主意。实际上,在Java中,尝试这样做会导致引发异常。但是让我们将forEach
转换成老式的for循环,也许您会发现问题所在。
for (let i = 0; i < a.length; ++i) {
a.pop();
}
现在更清楚了吗?每次迭代时,将最后一个元素弹出时,都会将数组的长度缩短1。因此,循环将在迭代一半以上的元素之后结束-因为到那时,它将也删除一半的元素,从而导致i
的值大于数组的当前长度。
使用forEach
时会发生同样的事情:弹出时,每次迭代都会缩短数组,导致循环仅在迭代了一半元素后终止。换句话说,随着数组缩小,迭代器变量将向前移动超过数组的末端。
答案 1 :(得分:1)
让我们这样做:
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);
您的索引在增加,但是长度在减少,因此,当索引在第一个元素中时,您将删除最后一个元素,因此删除数组的右半部分将是结果。
相同:迭代索引以一种方式进行,长度以另一种方式进行,因此整个过程在工作中期停止:
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
元素调用回调。