循环删除数组内符合规则的元素
在 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]。
- 当
i=1时,删除了第一个3。 - 数组变成
[1, 3, 4],长度缩短,后面的元素前移。 - 下一次循环
i变成了2。 - 此时
arr[2]是4,原本在第二个位置的3被跳过了,因为没有被检查到。
总结
| 需求 | 推荐方法 | 是否修改原数组 | 代码复杂度 |
|---|---|---|---|
| 获取新数组 | filter() | 否 (返回新数组) | ⭐ (最低) |
| 修改原数组 | 倒序 for 循环 | 是 (原地修改) | ⭐⭐ |
| 修改原数组 | while 循环 | 是 (原地修改) | ⭐⭐ |
最佳实践:优先使用 filter(),代码最简洁且不易出错。如果确实需要修改原数组(例如在超大数组中为了节省内存),请使用倒序循环。 |