ES6 中的 Set 和 Map 都是用于存储数据的集合类型,但它们在用途、数据结构和操作方式上有显著区别。以下是它们的核心差异:
1. 数据存储类型
-
Set
- 存储唯一值(重复值会被自动去重)。
- 值可以是任意类型(对象、原始值等)。
- 类似数组,但成员唯一且无序(ES6 规范未定义顺序,但现代浏览器通常按插入顺序存储)。
- 示例:
const set = new Set([1, 2, 2, 'a']); // Set {1, 2, 'a'}
-
Map
- 存储键值对(
key-value),键和值都可以是任意类型。 - 键是唯一的(重复键会覆盖之前的值)。
- 类似对象,但键不限于字符串(可以是对象、函数等)。
- 示例:
const map = new Map([['a', 1], [1, 'b'], [{x: 1}, 'c']]); // Map { 'a' => 1, 1 => 'b', {x: 1} => 'c' }
- 存储键值对(
2. 键(Key)的处理
-
Set
- 没有键的概念,只有值(可以理解为值本身既是键也是值)。
- 通过值来检查存在性或删除:
set.has(2); // true set.delete('a');
-
Map
- 通过键来访问或操作值:
map.get('a'); // 1 map.set('newKey', 'value'); map.delete(1); // 删除键为 1 的项
- 通过键来访问或操作值:
3. 性能与使用场景
-
Set
- 适合需要快速去重或检查值是否存在的场景(如黑名单、去重数组)。
- 操作复杂度:
- 添加/删除/检查:平均 O(1)。
-
Map
- 适合需要关联键值对的场景(如缓存、字典、计数器)。
- 相比普通对象(
{}),Map 的键更灵活,且能保持插入顺序。 - 操作复杂度:
- 添加/删除/检查:平均 O(1)。
4. 迭代方式
-
Set
- 迭代时直接获取值:
set.forEach(value => console.log(value)); for (const value of set) { /* ... */ }
- 迭代时直接获取值:
-
Map
- 迭代时可以分别获取键和值:
map.forEach((value, key) => console.log(key, value)); for (const [key, value] of map) { /* ... */ }
- 迭代时可以分别获取键和值:
5. 与对象的区别
-
Set vs 对象
- 对象通过属性名(字符串/Symbol)访问值,而 Set 通过值本身操作。
- 对象无法直接判断某个值是否作为属性存在(需
Object.prototype.hasOwnProperty),而 Set 的has()方法更直观。
-
Map vs 对象
- 对象的键只能是字符串或 Symbol,而 Map 的键可以是任意类型。
- Map 维护插入顺序,而普通对象的属性顺序在 ES6 前是不确定的(ES6 后部分场景有规定顺序)。
- Map 的实例方法(如
size、clear())比操作对象属性更方便。
总结表
| 特性 | Set | Map |
|---|---|---|
| 存储内容 | 唯一值 | 唯一键值对 |
| 键的类型 | 无(值即键) | 任意类型 |
| 去重 | 自动去重 | 键唯一,值可重复 |
| 主要方法 | add(), has(), delete() | set(), get(), has() |
| 迭代返回值 | 值 | [key, value] 数组 |
| 典型用途 | 去重、集合操作 | 字典、缓存、计数器 |
示例对比
// Set 示例 const uniqueNumbers = new Set([1, 2, 2, 3]); uniqueNumbers.add(4); console.log(uniqueNumbers.has(2)); // true // Map 示例 const userRoles = new Map(); userRoles.set('Alice', 'Admin'); userRoles.set('Bob', 'User'); console.log(userRoles.get('Alice')); // 'Admin'
根据需求选择:需要唯一值集合用 Set,需要键值对关联用 Map。