在使用 for...of
循环遍历 Set 时删除元素需要特别小心,因为直接在遍历过程中修改 Set 的结构可能会导致意外结果(如跳过元素或引发错误)。以下是几种安全的删除方法及注意事项:
一、错误示例:直接在循环中删除当前元素
const set = new Set([1, 2, 3, 4]);
for (const num of set) {
if (num > 2) {
set.delete(num); // 错误:可能跳过元素
}
}
console.log([...set]); // 输出: [1, 3](3 未被删除!)
问题原因:
删除元素后,Set 的内部迭代器位置可能发生变化,导致某些元素被跳过。
二、安全方法:先收集待删除元素,再批量删除
步骤:
- 遍历 Set,将需要删除的元素存入临时数组。
- 遍历临时数组,批量删除元素。
const set = new Set([1, 2, 3, 4]);
const toDelete = [];
// 第一步:收集需要删除的元素
for (const num of set) {
if (num > 2) {
toDelete.push(num);
}
}
// 第二步:批量删除
toDelete.forEach(num => set.delete(num));
console.log([...set]); // 输出: [1, 2]
三、转换为数组后遍历删除
步骤:
- 将 Set 转换为数组(如
[...set]
)。 - 遍历数组,使用原 Set 的
delete
方法删除元素。
const set = new Set([1, 2, 3, 4]);
[...set].forEach(num => {
if (num > 2) {
set.delete(num); // 安全:操作的是原 Set,但遍历的是副本
}
});
console.log([...set]); // 输出: [1, 2]
四、使用 while
循环手动控制迭代
步骤:
- 获取 Set 的迭代器。
- 手动调用
next()
,在删除元素后重置迭代器或继续处理。
const set = new Set([1, 2, 3, 4]);
const iterator = set[Symbol.iterator]();
let result;
while ((result = iterator.next()) && !result.done) {
const num = result.value;
if (num > 2) {
set.delete(num); // 删除当前元素
// 注意:此处迭代器可能已失效,需重新获取或跳过逻辑
}
}
console.log([...set]); // 输出: [1, 2]
五、注意事项与最佳实践
避免在
for...of
中直接删除:
直接删除会导致迭代器状态异常,优先选择批量删除或转换为数组操作。性能考虑:
- 若 Set 较大,批量删除(方法二)可能比转换为数组(方法三)更高效,因为避免了创建临时数组。
删除后重置迭代器:
在手动控制迭代(方法四)时,删除元素后需谨慎处理迭代器,可能需要重新获取迭代器或调整循环逻辑。适用场景:
- 批量删除:方法二或方法三。
- 复杂条件删除:结合
Array.filter()
或手动迭代(方法四)。
总结
安全删除 Set 元素的核心原则是:避免在遍历原 Set 的同时修改其结构。通过临时存储待删除元素或转换为数组,可以有效规避迭代器异常问题。