我知道这是一个令人讨厌的问题,但是有人可以解释我为什么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: - 然后删除索引6,它不存在:
[12, 3, 4, 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 ]