jym,先祝大家圣诞快乐,今天分享的知识是JS中的Map类型和Set类型。
Map 和 Set 是 ES6 新增的集合类型(区别于传统的 Object 和 Array),用于更高效地存储和管理数据,解决了传统数据结构的诸多局限,二者在存储结构、用途上有明确差异。
一、Set 类型:无序不重复的集合
1. 核心定义
Set 是一种 无序、唯一(不允许重复元素) 的集合数据结构,仅存储「值(value)」,不存储键值对,适合用于去重、快速判断元素是否存在等场景。
2. 核心特性
- 唯一性:自动过滤重复元素,添加重复值时不会报错,但不会生效;
- 无序性:元素不按插入顺序维护索引(无法通过下标访问,需通过遍历获取);
- 存储类型:可存储任意类型的值(原始类型 + 引用类型),判断重复时采用「SameValueZero」算法(类似
===,但NaN视为相等); - 无键名:仅存值,没有像 Object 那样的键名映射关系。
3. 常用 API
| API 方法 / 属性 | 功能说明 | 示例 |
|---|---|---|
new Set([iterable]) | 构造函数,创建 Set 实例,可传入可迭代对象(如数组)初始化 | const set = new Set([1, 2, 2, 3]); // Set(3) {1, 2, 3} |
size | 属性,返回 Set 中元素的数量 | set.size; // 3 |
add(value) | 添加元素,返回 Set 实例(支持链式调用) | set.add(4).add(5); // Set(5) {1, 2, 3, 4, 5} |
delete(value) | 删除指定元素,返回布尔值(成功删除为 true) | set.delete(2); // true |
has(value) | 判断元素是否存在,返回布尔值 | set.has(3); // true |
clear() | 清空 Set 中所有元素,无返回值 | set.clear(); // Set(0) {} |
keys()/values() | 返回迭代器,遍历所有元素(Set 无键,keys() 等同于 values()) | for (let val of set.values()) { console.log(val); } |
entries() | 返回迭代器,遍历 [value, value] 格式的数组 | for (let [val, val2] of set.entries()) { console.log(val === val2); // true } |
forEach(callback) | 遍历所有元素,回调函数接收 (value, key, set)(key 与 value 一致) | set.forEach((val) => console.log(val)); |
4. 典型应用场景
-
数组去重(最常用):
const arr = [1, 2, 2, 3, 3, 3]; const uniqueArr = [...new Set(arr)]; // [1, 2, 3] -
快速判断元素是否存在(比
Array.includes()效率更高,尤其数据量大时):const userIds = new Set([101, 102, 103]); console.log(userIds.has(102)); // true(O(1) 时间复杂度,数组是 O(n)) -
存储不重复的标签 / 筛选条件:
const tags = new Set(); tags.add("前端").add("JavaScript").add("前端"); // 最终仅存 ["前端", "JavaScript"]
二、Map 类型:键值对集合(升级版 Object)
1. 核心定义
Map 是一种有序、键值对的集合数据结构,键(key)和值(value)可以是任意类型(解决了 Object 键只能是字符串 / Symbol 的局限),适合用于存储需要保留插入顺序、键类型多样的映射关系。
2. 核心特性
- 键的多样性:键可以是任意类型(原始类型:字符串、数字、布尔值、
null、undefined、Symbol;引用类型:对象、数组等); - 有序性:按元素插入顺序存储,遍历顺序与插入顺序一致(Object 在 ES6 前无序,后续虽保留插入顺序,但有例外);
- 唯一性:键具有唯一性,重复设置同一键会覆盖原有值(判断键重复的规则同 Set,采用「SameValueZero」算法);
- 可迭代性:直接支持迭代器遍历,无需像 Object 那样先获取键数组再遍历。
3. 常用 API
| API 方法 / 属性 | 功能说明 | 示例 |
|---|---|---|
new Map([iterable]) | 构造函数,创建 Map 实例,可传入二维数组(键值对)初始化 | const map = new Map([["name", "张三"], ["age", 25]]); |
size | 属性,返回 Map 中键值对的数量 | map.size; // 2 |
set(key, value) | 设置键值对,返回 Map 实例(支持链式调用) | map.set("gender", "男").set("city", "北京"); |
get(key) | 获取指定键对应的值,不存在则返回 undefined | map.get("name"); // "张三" |
delete(key) | 删除指定键值对,返回布尔值(成功删除为 true) | map.delete("age"); // true |
has(key) | 判断键是否存在,返回布尔值 | map.has("city"); // true |
clear() | 清空 Map 中所有键值对,无返回值 | map.clear(); // Map(0) {} |
keys() | 返回迭代器,遍历所有键 | for (let key of map.keys()) { console.log(key); } |
values() | 返回迭代器,遍历所有值 | for (let val of map.values()) { console.log(val); } |
entries() | 返回迭代器,遍历 [key, value] 格式的数组 | for (let [key, val] of map.entries()) { console.log(key, val); } |
forEach(callback) | 遍历所有键值对,回调函数接收 (value, key, map) | map.forEach((val, key) => console.log(key, val)); |
4. 典型应用场景
-
存储多样键类型的映射关系(如以对象为键):
const user1 = { id: 101 }; const user2 = { id: 102 }; const userInfoMap = new Map(); // 以对象为键,存储用户对应的信息 userInfoMap.set(user1, { name: "张三", age: 25 }); userInfoMap.set(user2, { name: "李四", age: 30 }); console.log(userInfoMap.get(user1)); // { name: "张三", age: 25 } -
保留插入顺序的键值对存储:
// Object 无法保证顺序(尤其数字键会优先排序),Map 可精准保留插入顺序 const map = new Map(); map.set(2, "b").set(1, "a").set("3", "c"); [...map.keys()]; // [2, 1, "3"](按插入顺序) -
替代 Object 存储大量动态键值对(便于增删查和遍历,比 Object 更高效):
// 接口返回的动态筛选条件,用 Map 存储更灵活 const filterMap = new Map(); filterMap.set("status", ["active", "pending"]) .set("date", ["2025-01-01", "2025-12-31"]) .set("type", "web"); // 快速遍历筛选条件 filterMap.forEach((val, key) => { console.log(`筛选条件 ${key}:`, val); });
三、Map vs Set vs Object vs Array 核心对比
| 数据结构 | 存储形式 | 核心特点 | 适用场景 |
|---|---|---|---|
| Set | 仅存值(无键) | 无序、不重复 | 数组去重、元素存在性判断、存储唯一标签 |
| Map | 键值对(键任意类型) | 有序、键唯一、可迭代 | 多样键映射、保留插入顺序的键值对、动态增删遍历 |
| Object | 键值对(键仅字符串 / Symbol) | 无序(ES6 后部分有序)、默认原型 | 简单键值对存储、对象属性配置 |
| Array | 索引 - 值(有序列表) | 有序、允许重复 | 有序数据集合、按索引访问、批量操作数据 |
四、关键注意事项
-
引用类型作为键 / 值的注意点:Set/Map 存储引用类型时,判断重复的是引用地址,而非对象内容:
const obj1 = { a: 1 }; const obj2 = { a: 1 }; const set = new Set([obj1, obj2]); console.log(set.size); // 2(obj1 和 obj2 引用地址不同,视为不同元素) -
Map 与 Object 的选择原则:
- 当键为非字符串 / Symbol 类型、需要保留插入顺序、动态增删大量键值对时,优先用 Map;
- 当仅存储简单配置项、键为字符串时,用 Object 更简洁。
-
Set 与 Array 的选择原则:
- 当需要去重、快速判断元素是否存在时,优先用 Set;
- 当需要按索引访问、批量排序 / 筛选数据时,用 Array。
总结
- Set:无序不重复的「值集合」,核心用途是去重和快速判存;
- Map:有序键值对的「映射集合」,是 Object 的升级版,支持任意类型键,适合复杂映射场景;
- 二者均支持迭代器遍历,
size属性获取长度,比传统 Object/Array 更高效、更灵活; - 选择逻辑:按「是否需要键值对」区分 Map/Set,按「是否需要去重 / 有序」区分 Set/Array、Map/Object。