for...of循环在遍历Set和Map时的注意事项有哪些?

简介: for...of循环在遍历Set和Map时的注意事项有哪些?

for...of 循环遍历 Set 和 Map 时的注意事项

一、遍历 Set 集合的注意事项

Set 是 JavaScript 中用于存储唯一值的有序集合,使用 for...of 遍历时需注意以下特性:

1. 遍历顺序与插入顺序一致

Set 集合遵循“插入顺序”,遍历时会按照元素添加的顺序依次输出。

const set = new Set([3, 1, 2]);
for (const item of set) {
   
  console.log(item); // 输出:3, 1, 2(与插入顺序一致)
}
2. 直接获取元素值,无需额外处理

Set 的每个元素本身就是值,for...of 可直接遍历元素,无需像数组一样通过索引获取。

const fruits = new Set(['apple', 'banana', 'orange']);
for (const fruit of fruits) {
   
  console.log(fruit); // 直接输出元素值
}
3. 无法通过索引操作元素

Set 没有索引概念,遍历时无法通过下标访问或修改元素(需通过 deleteclear 方法操作)。

const set = new Set([1, 2, 3]);
for (const item of set) {
   
  // 错误示例:无法通过索引修改
  // set[0] = 10; // 报错,Set 无索引属性
  if (item === 2) set.delete(item); // 正确方式:通过值删除
}
4. 与 forEach 的行为对比
  • for...of:可使用 break/continue 中断循环,支持 async/await
  • forEach:无法中断循环,需通过抛出异常终止。
    ```javascript
    const set = new Set([1, 2, 3, 4]);
    // for...of 可提前终止
    for (const item of set) {
    if (item === 3) break;
    console.log(item); // 输出:1, 2
    }

// forEach 无法直接终止,需抛出异常
set.forEach(item => {
if (item === 3) throw new Error('终止循环');
console.log(item);
});



##### 5. **遍历 Symbol 类型元素**
Set 支持存储 Symbol 类型,for...of 可正常遍历(需注意 Symbol 的唯一性)。
```javascript
const sym1 = Symbol('a');
const sym2 = Symbol('b');
const set = new Set([sym1, sym2]);
for (const sym of set) {
  console.log(sym); // 输出 Symbol(a), Symbol(b)
}

二、遍历 Map 集合的注意事项

Map 是键值对的有序集合,每个元素为 [key, value] 数组,for...of 遍历时需特殊处理键值对。

1. 直接获取键值对数组

for...of 遍历 Map 时,每个迭代项是包含 [key, value] 的数组,需解构赋值获取键值。

const map = new Map([
  ['name', 'John'],
  ['age', 30],
  ['city', 'New York']
]);

// 解构获取键值
for (const [key, value] of map) {
   
  console.log(`${
     key}: ${
     value}`); 
  // 输出:name: John, age: 30, city: New York
}
2. 遍历顺序与插入顺序一致

Map 同样遵循插入顺序,遍历时键值对的顺序与添加时一致。

const map = new Map();
map.set(2, 'two');
map.set(1, 'one');
map.set(3, 'three');

for (const [key, val] of map) {
   
  console.log(key); // 输出:2, 1, 3(插入顺序)
}
3. 结合 keys()/values()/entries() 方法

Map 提供三种遍历方法,需配合 for...of 使用:

  • map.keys():遍历所有键。
  • map.values():遍历所有值。
  • map.entries():遍历所有键值对(默认行为)。
    ```javascript
    // 遍历键
    for (const key of map.keys()) {
    console.log(key); // 2, 1, 3
    }

// 遍历值
for (const val of map.values()) {
console.log(val); // two, one, three
}

// 等价于直接遍历 map
for (const item of map.entries()) {
console.log(item); // [2, 'two'], [1, 'one'], [3, 'three']
}



##### 4. **键的类型支持**
Map 的键可以是任意类型(包括对象、函数、Symbol 等),for...of 遍历时需注意:
```javascript
const obj = { id: 1 };
const map = new Map();
map.set(obj, 'object-key');
map.set(Symbol('sym'), 'symbol-key');

for (const [key, val] of map) {
  console.log(`${typeof key}: ${val}`); 
  // 输出:object: object-key, symbol: symbol-key
}
5. 与 forEach 的区别
  • for...of:可通过解构获取键值,支持中断循环。
  • forEach:通过回调函数接收 (value, key, map) 三个参数,无法中断。
    map.forEach((value, key, map) => {
         
    console.log(`${
           key}: ${
           value}`);
    // 无法使用 break/continue,需通过逻辑控制是否继续
    });
    

三、通用注意事项(Set/Map 共通)

  1. 遍历期间修改集合的影响

    • 在 for...of 循环中添加或删除元素可能导致 不可预测的结果(不同浏览器实现可能不同),建议避免。
      const set = new Set([1, 2, 3]);
      for (const item of set) {
             
      if (item === 2) set.add(4); // 不建议在遍历中修改集合
      }
      
  2. 与迭代器的兼容性

    • Set 和 Map 都实现了 Iterator 接口,for...of 本质上是对迭代器的封装,可配合 next() 方法手动控制遍历。
      const set = new Set([1, 2, 3]);
      const iterator = set[Symbol.iterator]();
      console.log(iterator.next()); // { value: 1, done: false }
      
  3. 空集合的处理

    • 当 Set 或 Map 为空时,for...of 循环不会执行任何代码块,无需额外判断 size === 0

四、最佳实践建议

  1. Set 遍历场景

    • 当需要按插入顺序遍历唯一值时,直接使用 for...of。
    • 若需过滤元素,可结合 Array.from(set).filter() 转换为数组处理。
  2. Map 遍历场景

    • 使用解构赋值 [key, value] 同时获取键值对。
    • 若仅需键或值,优先使用 keys()values() 提升可读性。
  3. 避免遍历中修改集合

    • 如需修改,建议先将集合转换为数组(如 [...set]),再遍历处理。

通过理解 for...of 在 Set 和 Map 中的遍历特性,可更高效地处理集合数据,同时避免因特性不熟悉导致的逻辑错误。

相关文章
|
5天前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
19 1
|
27天前
|
安全 Java 数据库连接
让我们讲解一下 Map 集合遍历的方式
我是小假 期待与你的下一次相遇 ~
79 43
|
1月前
|
存储 安全
for...of循环在遍历Set时,如何正确添加元素?
for...of循环在遍历Set时,如何正确添加元素?
142 59
|
1月前
|
存储 安全 JavaScript
如何在不影响遍历的情况下从Set中删除元素?
如何在不影响遍历的情况下从Set中删除元素?
135 58
|
1月前
|
存储 安全
for...of循环遍历Set时,如何删除元素?
for...of循环遍历Set时,如何删除元素?
153 57
|
5天前
|
存储 缓存 JavaScript
在JavaScript中,Set和Map的性能有什么区别?
在JavaScript中,Set和Map的性能有什么区别?
27 0
|
4月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
115 2
|
1月前
|
存储 C++ 容器
unordered_set、unordered_multiset、unordered_map、unordered_multimap的介绍及使用
unordered_set是不按特定顺序存储键值的关联式容器,其允许通过键值快速的索引到对应的元素。在unordered_set中,元素的值同时也是唯一地标识它的key。在内部,unordered_set中的元素没有按照任何特定的顺序排序,为了能在常数范围内找到指定的key,unordered_set将相同哈希值的键值放在相同的桶中。unordered_set容器通过key访问单个元素要比set快,但它通常在遍历元素子集的范围迭代方面效率较低。它的迭代器至少是前向迭代器。前向迭代器的特性。
75 0
|
1月前
|
编译器 C++ 容器
用一棵红黑树同时封装出map和set
再完成上面的代码后,我们的底层代码已经完成了,这时候已经是一个底层STL的红黑树了,已经已符合库里面的要求了,这时候我们是需要给他穿上对应的“衣服”,比如穿上set的“衣服”,那么这个穿上set的“衣服”,那么他就符合库里面set的要求了,同样map一样,这时候我们就需要实现set与map了。因此,上层容器map需要向底层红黑树提供一个仿函数,用于获取T当中的键值Key,这样一来,当底层红黑树当中需要比较两个结点的键值时,就可以通过这个仿函数来获取T当中的键值了。我们就可以使用仿函数了。
30 0
|
1月前
|
存储 编译器 容器
set、map、multiset、multimap的介绍及使用以及区别,注意事项
set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可以得到有序序列。set当中存储元素的value都是唯一的,不可以重复,因此可以使用set进行去重。set默认是升序的,但是其内部默认不是按照大于比较,而是按照小于比较。set中的元素不能被修改,因为set在底层是用二叉搜索树来实现的,若是对二叉搜索树当中某个结点的值进行了修改,那么这棵树将不再是二叉搜索树。
71 0