两个新集合类型:Set 是"自动去重的袋子"——你往里扔重复的东西它只留一个,专门解决去重;Map 是"钥匙随便的柜子"——普通对象的钥匙只能是字符串,Map 的钥匙数字、对象、数组都能当。
以前去重得写一个空数组,遍历原数组,每看到一个新值才 push 进去——啰嗦。new Set(数组) 自动去重,再用 [...] 展开回数组,一行搞定。
let arr = [1, 2, 3, 4, 4, 3]; // 有重复的数组
// 以前:循环 + indexOf 判断,啰嗦
let unique = []; // 先开个空数组
arr.forEach(x => { // 挨个遍历
if (unique.indexOf(x) === -1) unique.push(x); // 没见过才加
});
// 现在:new Set 自动去重 + ... 展开回数组
let unique = [...new Set(arr)]; // Set 自动去重,... 再拆回数组
// ^^^ 把 Set 重新拆成数组
console.log(unique); // [1,2,3,4]
new Set(数组) 自动去重,[...set] 再变回数组。这是最常用的数组去重写法,面试必背。
往袋子里扔(add)、拿出来看有没有(has)、扔掉一个(delete)、倒空(clear)、数个数(size)。
let s = new Set(); // 开个空袋子
s.add("大哥"); // 扔进去
s.add("二哥"); // 再扔一个
s.add("大哥"); // 重复的扔不进去,袋子自动忽略
s.size; // 2(袋子里现在有几个)
s.has("大哥"); // true(有没有这个)
s.delete("二哥"); // 扔掉一个 → true
s.clear(); // 整个袋子倒空
普通对象有个坏习惯:你拿数字当钥匙,它偷偷帮你转成字符串;想拿对象当钥匙?门都没有。Map 不转类型,钥匙原样保留。
// 普通对象:键强制转字符串
let obj = {}; // 普通对象
obj[123] = "数字键"; // 拿数字 123 当键
console.log(obj); // {"123": "数字键"} ← 123 被转成 "123" 了
// Map:键保持原样,对象也能当键
let m = new Map(); // 开个 Map
m.set(123, "数字键"); // 数字键
m.set({ id: 1 }, "对象键"); // 对象当键,普通对象做不到!
console.log(m.get(123)); // 数字键
Map 的键可以是数字、对象、数组,不会像普通对象那样被强制转成字符串。动态增删键值对、需要用对象/数字当 key 的场景,用 Map 最合适。
let m = new Map(); // 开个空 Map
m.set("name", "大喇叭"); // 放一对键值
m.get("name"); // 拿值 → 大喇叭
m.has("name"); // 有没有 → true
m.delete("name"); // 删掉一对 → true
m.size; // 有几对
m.clear(); // 全清空
| 对比项 | 普通对象 | Map |
|---|---|---|
| 键的类型 | 只能字符串 / 符号 | 任意类型(数字、对象都行) |
| 遍历 | for...in 比较绕 | forEach 直接遍历,按插入顺序 |
| 常用场景 | 字段固定的数据(如 User) | 动态增删的键值对 |
① 可能在什么地方用:数组去重 [...new Set(arr)]、两个数组求交集/并集、用对象当 key 存关联数据、记录用户访问过的页面。
② 常见的问题:把 Set 当数组直接用 arr[i] 取下标(Set 没有下标);用普通对象存对象键导致 key 全变字符串;遍历 Map 用 for...in 遍历不到。
③ 解决思路:Set 要取下标就先 [...set] 展开;Map 用 m.forEach 或 for...of m;需要对象键就用 Map,别用普通对象。
[...new Set(arr)]。