在前面聊for of遍历的时候,我提到了Set和Map这两个ES6新增的重要数据结构,它们弥补了传统数组、对象的诸多不足,也是前端面试高频考点。今天就用最接地气的方式,讲清楚Set/Map的定义、用法和核心区别。
一、先搞懂:Set 是什么?
Set 翻译过来是“集合”,它是一种存储唯一值的无序集合 —— 简单说,就是里面的元素不会重复,而且没有“键值对”的概念,只有“值”本身。
1. Set 核心特点
- 元素唯一不重复:自动去重,哪怕你添加重复值,也只会保留一个
- 无序:不会按照插入顺序以外的规则排序(但实际遍历会按插入顺序)
- 可以存储任意类型的值(原始类型、引用类型都可以)
- 没有索引:不能像数组那样用
[0]取值,只能通过遍历获取
2. Set 常用场景 & 示例
最典型的场景就是数组去重、存储不重复的标签/ID 等。
// 1. 基础用法:创建Set并添加元素
const blogTags = new Set();
// 添加元素
blogTags.add("JavaScript");
blogTags.add("前端");
blogTags.add("JavaScript"); // 重复值,不会被存储
blogTags.add(123); // 支持不同类型值
console.log(blogTags); // Set(3) { 'JavaScript', '前端', 123 }
console.log(blogTags.size); // 3(获取元素数量,类似数组length)
// 2. 常用方法
console.log(blogTags.has("前端")); // true(判断是否包含某元素)
blogTags.delete("前端"); // 删除指定元素
console.log(blogTags); // Set(2) { 'JavaScript', 123 }
blogTags.clear(); // 清空所有元素
console.log(blogTags); // Set(0) {}
// 3. 核心场景:数组去重
const duplicateArr = ["JS", "前端", "JS", "遍历", "前端"];
const uniqueArr = [...new Set(duplicateArr)]; // 转成数组
console.log(uniqueArr); // ['JS', '前端', '遍历']
二、再理清:Map 是什么?
Map 翻译过来是“映射”,它是一种键值对集合,可以理解为“升级版的普通对象”—— 解决了普通对象“键只能是字符串/符号”的痛点。
1. Map 核心特点
- 键值对结构:和对象类似,但键可以是任意类型(数字、布尔、对象、数组都能当键)
- 有序:遍历顺序和插入顺序一致(普通对象不保证顺序)
- 可迭代:天然支持
for of遍历,无需转换 - 可以通过
size直接获取键值对数量(普通对象需要手动统计)
2. Map 常用场景 & 示例
适合需要用非字符串做键、频繁增删键值对、需要有序遍历键值对的场景(比如存储用户信息、缓存数据等)。
// 1. 基础用法:创建Map并添加键值对
const blogComments = new Map();
// 键可以是任意类型
const user1 = { id: 1, name: "张三" };
blogComments.set("title", "JS遍历教程"); // 字符串键
blogComments.set(user1, "写得太清楚了!"); // 对象作为键
blogComments.set(100, "阅读量破百"); // 数字作为键
console.log(blogComments);
// Map(3) {
// 'title' => 'JS遍历教程',
// { id: 1, name: '张三' } => '写得太清楚了!',
// 100 => '阅读量破百'
// }
console.log(blogComments.size); // 3
// 2. 常用方法
console.log(blogComments.get("title")); // 'JS遍历教程'(获取值)
console.log(blogComments.has(user1)); // true(判断是否存在该键)
blogComments.delete(100); // 删除指定键值对
console.log(blogComments); // Map(2) { 'title' => 'JS遍历教程', { id: 1, name: '张三' } => '写得太清楚了!' }
// blogComments.clear(); // 清空所有键值对
// 3. 遍历Map(for of 直接用)
for (const [key, value] of blogComments) {
console.log(`键:${JSON.stringify(key)},值:${value}`);
}
3. Map vs 普通对象(补充)
很多人会问“为啥不用对象非要用Map?”,看这个对比就懂了:
| 特性 | 普通对象 | Map |
|---|---|---|
| 键的类型 | 只能是字符串/符号 | 任意类型(对象、数字等) |
| 顺序 | 不保证插入顺序 | 严格按插入顺序遍历 |
| 长度获取 | 需手动遍历统计 | 直接用size属性 |
| 迭代 | 需转换(Object.entries) | 原生支持for of遍历 |
| 性能 | 增删频繁时性能一般 | 增删频繁时性能更优 |
三、Set & Map 核心区别(一张表讲透)
| 维度 | Set | Map |
|---|---|---|
| 核心结构 | 单值集合(只有值,无键) | 键值对集合(键+值) |
| 核心作用 | 存储不重复的唯一值 | 存储键值对(键可任意类型) |
| 去重特性 | 自动去重(核心能力) | 键唯一(值可重复) |
| 常用方法 | add()、has()、delete()、size | set()、get()、has()、delete()、size |
| 典型场景 | 数组去重、标签去重、存储唯一ID | 非字符串键的键值对、有序键值对、高频增删的缓存 |
四、实战小例子:用Set/Map解决实际问题
// 1. 用Set筛选出两篇博客的共同标签
const blog1Tags = new Set(["JS", "前端", "遍历"]);
const blog2Tags = new Set(["前端", "性能", "JS"]);
const commonTags = [...blog1Tags].filter(tag => blog2Tags.has(tag));
console.log("共同标签:", commonTags); // ['JS', '前端']
// 2. 用Map存储用户的阅读记录(键是用户对象,值是阅读时间)
const userA = { name: "李四" };
const userB = { name: "王五" };
const readRecords = new Map();
readRecords.set(userA, "2026-03-03 10:00");
readRecords.set(userB, "2026-03-03 11:30");
// 快速获取指定用户的阅读时间
console.log(userA.name + "的阅读时间:", readRecords.get(userA)); // 李四的阅读时间:2026-03-03 10:00
总结
- Set 是“无重复值的集合”,核心能力是去重,只有值没有键,适合存储无需重复的单列数据;
- Map 是“升级版键值对”,核心能力是支持任意类型键 + 有序遍历,适合存储需要键值关联的复杂数据;
- 记住核心区别:Set管“唯一值”,Map管“灵活的键值对”,根据数据类型和场景选择即可。





