Skip to content

循环删除数组内符合规则的元素

在 JavaScript 中,绝对不要forEach 或普通的 for 循环中直接删除数组元素(使用 splice),这会导致索引错位,从而跳过元素或产生不可预知的结果。 这里有三种最常用且安全的方法:

方法一:使用 filter (推荐,最优雅)

这是处理数组过滤的标准方法。它不会修改原数组,而是返回一个新数组

  • 适用场景:你需要得到一个过滤后的新数组,不介意原数组被保留。
javascript
const numbers = [1, 2, 3, 4, 5];
const target = 3;
// 创建新数组,只保留不等于 target 的元素
const newArr = numbers.filter(item => item !== target);
console.log(newArr); // [1, 2, 4, 5]
console.log(numbers); // 原数组不变: [1, 2, 3, 4, 5]

方法二:倒序循环遍历 (原数组修改)

如果你必须修改原数组(为了节省内存或保持引用一致),使用 for 循环从后往前遍历。

  • 原理:从数组末尾开始删除,删除后面的元素不会影响前面元素的索引。
  • 适用场景:必须在原数组上操作,且性能敏感。
javascript
const numbers = [1, 2, 3, 4, 5];
// 倒序:i = length - 1; i >= 0; i--
for (let i = numbers.length - 1; i >= 0; i--) {
  if (numbers[i] === 3) {
    numbers.splice(i, 1); // 删除当前索引的元素
  }
}
console.log(numbers); // [1, 2, 4, 5] (原数组已改变)

方法三:使用 while 循环 (原数组修改)

这是另一种修改原数组的方法,逻辑也很简单。每次找到符合条件的元素并删除后,索引 i 不增加,因为数组长度变短了,下一个元素会自动补位到当前索引 i

  • 适用场景:需要在原数组上操作,且习惯使用 while
javascript
const numbers = [1, 2, 3, 3, 4, 5];
let i = 0;
while (i < numbers.length) {
  if (numbers[i] === 3) {
    numbers.splice(i, 1); 
    // 注意:这里不执行 i++,因为删除后后面的元素前移,下一个要检查的依然是位置 i
  } else {
    i++; // 只有没删除时,才向后移动
  }
}
console.log(numbers); // [1, 2, 4, 5]

❌ 错误示范:正向 for 循环 (陷阱)

千万不要这样做:

javascript
// 错误代码示例
for (let i = 0; i < numbers.length; i++) {
  if (numbers[i] === 3) {
    numbers.splice(i, 1); 
  }
}

原因: 假设数组是 [1, 3, 3, 4]

  1. i=1 时,删除了第一个 3
  2. 数组变成 [1, 3, 4],长度缩短,后面的元素前移。
  3. 下一次循环 i 变成了 2
  4. 此时 arr[2]4,原本在第二个位置的 3 被跳过了,因为没有被检查到。

总结

需求推荐方法是否修改原数组代码复杂度
获取新数组filter()否 (返回新数组)⭐ (最低)
修改原数组倒序 for 循环是 (原地修改)⭐⭐
修改原数组while 循环是 (原地修改)⭐⭐
最佳实践:优先使用 filter(),代码最简洁且不易出错。如果确实需要修改原数组(例如在超大数组中为了节省内存),请使用倒序循环