在使用 for...of
循环遍历 Set 时添加元素需要格外谨慎,因为直接在遍历过程中修改 Set 的结构可能导致迭代器状态异常(如无限循环或跳过元素)。以下是安全添加元素的方法及注意事项:
一、错误示例:直接在循环中添加元素
const set = new Set([1, 2, 3]);
for (const num of set) {
set.add(num + 1); // 错误:可能导致无限循环或异常
console.log(num);
}
// 输出: 1, 2, 3(部分浏览器可能陷入无限循环)
问题原因:
添加元素会改变 Set 的内部结构,导致迭代器无法正确跟踪元素位置,可能引发不可预测的行为。
二、安全方法:先完成遍历,再添加元素
核心思路:将添加逻辑与遍历逻辑分离,避免同时操作。
const set = new Set([1, 2, 3]);
// 第一步:先遍历处理
for (const num of set) {
console.log(num); // 输出: 1, 2, 3
}
// 第二步:遍历结束后添加元素
set.add(4);
set.add(5);
console.log([...set]); // 输出: [1, 2, 3, 4, 5]
三、使用临时数组存储待添加元素
步骤:
- 遍历 Set 并记录需要添加的元素到临时数组。
- 遍历结束后,批量添加临时数组中的元素。
```javascript
const set = new Set([1, 2, 3]);
const toAdd = [];
// 遍历并收集待添加元素
for (const num of set) {
if (num > 1) {
toAdd.push(num + 10); // 例如:2→12,3→13
}
}
// 批量添加
toAdd.forEach(val => set.add(val));
console.log([...set]); // 输出: [1, 2, 3, 12, 13]
### **四、转换为数组后遍历并操作原 Set**
**步骤**:
1. 将 Set 转换为数组(创建副本)。
2. 遍历数组,向原 Set 中添加元素(此时遍历的是副本,不影响原 Set 的迭代状态)。
```javascript
const set = new Set([1, 2, 3]);
[...set].forEach(num => {
set.add(num * 10); // 向原 Set 添加元素
console.log(num); // 遍历的是数组副本,不受添加操作影响
});
console.log([...set]); // 输出: [1, 2, 3, 10, 20, 30]
五、注意事项与最佳实践
绝对避免遍历与修改同时进行:
任何在for...of
循环中直接添加元素的操作都可能引发异常,必须分离遍历和修改逻辑。临时存储的必要性:
- 若添加逻辑依赖遍历过程中的数据(如根据当前元素计算新元素),需先用临时数组存储待添加项,再批量操作。
性能考量:
- 转换为数组(方法四)会产生额外内存开销,若 Set 较大,优先使用临时数组收集(方法三)。
特殊场景:无限循环风险
若添加的元素与原 Set 中的元素存在包含关系(如添加num
到Set([num])
),可能导致无限循环,需严格控制添加逻辑。
六、总结:安全添加的核心原则
- 逻辑分离:遍历阶段仅负责“读”操作,添加阶段仅负责“写”操作。
- 使用副本:通过数组副本或临时变量避免直接修改原 Set 的结构。
- 批量操作:收集所有待添加元素后,一次性批量添加,减少对迭代器的影响。
遵循以上原则,可在不破坏遍历逻辑的前提下安全地向 Set 中添加元素,避免因异步操作或状态异常导致的程序错误。