JavaScript -使用array .prototype.splice从数组中删除元素时出现意外结果



我知道这是一个令人讨厌的问题,但是有人可以解释我为什么splice方法以一种奇怪的方式执行。请向我解释为什么预期输出与实际结果不同。


let numbers = [15, 12, 15, 3, 5, 4, 6];
// Get the indexes of the numbers greater than 5
let indexes = numbers.reduce((arr, current, index) => {
if (current > 5) {
arr.push(index);
}
return arr;
}, []);
// Loop through the indexes while removing the indexes from the numbers array
indexes.forEach((element) => {
numbers.splice(element, 1);
});
// expected result: numbers = [ 3 , 5, 4 ];
// actual result: numbers = [ 12, 3, 4, 6 ]

.splice()改变了它所使用的数组。你可能已经知道了这一点,但是如果你用console.log调试你的代码,你会看到发生了什么;简而言之,你的第一个号码>5等于15。15在索引0处,所以移除索引0。然而,当splice改变它所使用的数组时,12变成索引0,然后第二个15变成索引1,以此类推。例如,您的代码有以下索引:0、1、2、6。

  • 第一次删除索引0:[12, 15, 3, 5, 4, 6]
  • 然后删除索引1:[12, 3, 5, 4, 6]
  • 然后删除索引2:[12, 3, 4, 6]
  • 然后删除索引6,它不存在:[12, 3, 4, 6]

实现这一目标的更好方法是使用.filter()。Filter创建一个包含所有通过回调中给出的测试的项目的新数组,因此:

numbers = numbers.filter((num) => num < 6);

这是箭头函数表达式的简写,只返回小于6的数字。

splice实际上删除了条目。它不会创建array的任何副本。在您的示例中,在reduce操作之后,索引将是

 [0, 1, 2, 6]

,然后在迭代和拼接时,在第一次迭代中删除位置为0的数组,因此数组变为

numbers = [12, 15, 3, 5, 4, 6];

的长度也减少了。在forEach的下一次迭代中,索引位置为1的数组元素被删除,在我们的例子中是15。所以在第二次迭代后数组变成

numbers = [12, 3, 5, 4, 6];

同样,在下一次迭代中,您将得到类似

的结果
[12, 3, 4, 6]

正如有人提到的,问题是在每次迭代中对变化的数组应用更改。

我认为这个例子是为了学习的目的,因为这样写会更容易:


let numbers = [15, 12, 15, 3, 5, 4, 6]
numbers.filter(elem => elem <= 5) 

在任何情况下,遵循演示代码,最好强调容易产生怪异效果的突变的危险性。我以更实用的风格重写了代码:

let numbers = [15, 12, 15, 3, 5, 4, 6];
// Get the indexes of the numbers greater than 5
let indexes = numbers.reduce((arr, current, index) => {
if (current > 5) {
return arr.concat(index);
}
return arr;
}, []);
// Instead of removing we create a new array filtering out the elements we dont want
let filteredNumbers = numbers.filter((_,index) => indexes.indexOf(index) === -1)
console.log(filteredNumbers)
// expected result: numbers = [ 3 , 5, 4 ];
// actual result: numbers = [ 3, 5, 4 ]

最新更新