返回博客

开发小透明 & 面试大明星:Map 和 Set 的双面人生

开发小透明 & 面试大明星:Map 和 Set 的双面人生。 在 JavaScript 的江湖中,有两位常被误解的角色:* **开发中:小透明,不出场也没人想念它们*** **面试时:大明星,不问它都觉得不专业**

开发小透明 & 面试大明星:Map 和 Set 的双面人生
大约 23 小时前
5 分钟阅读
82 次浏览

开发小透明 & 面试大明星:Map 和 Set 的双面人生

在 JavaScript 的江湖中,有两位常被误解的角色:

  • 开发中:小透明,不出场也没人想念它们
  • 面试时:大明星,不问它都觉得不专业

它们就是 —— Map 和 Set

如果你觉得这两个 ES6 新数据结构“偶尔能用,用了也没啥感觉”,那么这篇文章会刷新你的认知: 它们不只是“高级玩意儿”,而是现代前端工程化的基石数据结构之一

本文我会从 故事 → 行为 → 实战 → 底层 → 最佳实践 五个维度带你把它们吃透。

一、为什么开发里它们像“小透明”?

因为过去我们一直在用以下组合:

  • Array + 去重
  • Object + 查找
  • Object + KV 存储
  • Array + indexOf 判断重复
  • JSON 结构来做 Map(笑)

这些组合当然能用,但在 性能、语义、结构设计、扩展性 上, 已经远落后现代工程的需求。

ES6 给了我们真正的集合结构: Map = 高性能字典 Set = 保证唯一性的集合

但很多人没意识到“换个数据结构”能带来如此大的提升,于是:

你不知道它好,只是因为你长期将就。

二、Map:从“字典工具人”到“专业 Key-Value 管家”

在 Object 的世界里,key 只能是 字符串或 Symbol,而 Map 说:

key?只要你敢给,我就敢存。

对象、函数、NaN、DOM 节点、数字、正则…… 只要是引用,都能作为 key。

更贴近真实业务的数据关系

js
const users = new Map(); const userObj = { id: 1 }; users.set(userObj, { name: "Alice", age: 22 }); console.log(users.get(userObj)); // { name: 'Alice', age: 22 Object 中,这根本做不到,也不合理(只能把对象转成字符串强行塞进去)。 ## Map 的核心能力:不是“能存”,而是“存得专业” ### 1. 插入顺序有保证 ```js const m = new Map(); m.set('z', 1).set('a', 2).set('m', 3); for (const k of m.keys()) console.log(k); // z, a, m

Object 在这方面完全不可靠。

2. 高性能:频繁增删的王者

在 10 万次操作中,Map 通常比 Object 快一倍以上。

js
console.time('Map'); for (let i = 0; i < 100000; i++) { map.set(i, i); map.delete(i); } console.timeEnd('Map');

3. NaN === NaN?在 Map 里 Yes。

js
map.set(NaN, "OK"); console.log(map.get(NaN)); // OK

JS 自身都做不到的事情 Map 做到了,这很专业。

三、Set:唯一性的天生保障者(去重神器)

Set 是程序员的“保安”:

你们都能进来?不行,我要保证唯一。

js
const s = new Set([1,2,2,3]); console.log([...s]); // [1,2,3]

再也不用写:

js
arr.filter((v, i) => arr.indexOf(v) === i)

这种“过时检查方式”。

Set 的必会三板斧(面试高频)

1. 去重

js
[...new Set([1, 2, 2, 3])]

2. 集合运算:并集 / 交集 / 差集

js
const A = new Set([1,2,3]); const B = new Set([3,4,5]); // 并集 new Set([...A, ...B]); // 交集 new Set([...A].filter(x => B.has(x))); // 差集 new Set([...A].filter(x => !B.has(x)));

3. 判断是否存在(极快)

js
set.has(value)

比 Array.includes() 快得多。

四、开发为什么需要 Map / Set?

它们解决的是 数据结构设计的痛点

痛点 1:Object 当 Map 用的天然缺陷

  • key 不自由
  • 无序
  • 容易和原型链冲突
  • 无 size
  • 性能弱

痛点 2:Array 去重、查找都是 O(n)

Set/Map 基本都是 O(1)

痛点 3:有些业务天然是集合关系,而不是数组关系

五、真实业务场景:不懂 Map/Set 都会写更复杂的代码

以下全是真实项目中的“高频场景”。

场景 1:Session 管理系统(Map + Set)

js
class SessionManager { constructor() { this.sessions = new Map(); // userId -> sessionData this.activeUsers = new Set(); // 在线用户 } login(userId, sessionData) { this.sessions.set(userId, { ...sessionData, lastActive: Date.now() }); this.activeUsers.add(userId); } logout(userId) { this.sessions.delete(userId); this.activeUsers.delete(userId); } getOnlineUsers() { return [...this.activeUsers].map(id => this.sessions.get(id)); } }

用 Object 和 Array 实现这个? 更慢、更丑、更复杂。

场景 2:LRU 缓存(典型 Map 模式)

js
class LRUCache { constructor(limit = 100) { this.limit = limit; this.cache = new Map(); } get(key) { if (!this.cache.has(key)) return; const val = this.cache.get(key); this.cache.delete(key); this.cache.set(key, val); return val; } set(key, val) { if (this.cache.has(key)) this.cache.delete(key); else if (this.cache.size >= this.limit) { const oldest = this.cache.keys().next().value; this.cache.delete(oldest); } this.cache.set(key, val); } }

LRU 的底层 = 有序 Map + O(1) 操作 Object 根本做不到。

场景 3:权限系统(Set 作为权限集)

js
class Permission { constructor() { this.userRoles = new Map(); this.rolePermissions = new Map(); } assignRole(uid, role) { if (!this.userRoles.has(uid)) this.userRoles.set(uid, new Set()); this.userRoles.get(uid).add(role); } can(uid, perm) { const roles = this.userRoles.get(uid) || new Set(); for (const role of roles) { const perms = this.rolePermissions.get(role) || new Set(); if (perms.has(perm)) return true; } return false; } }

这就是 Map + Set 的典型搭配: 一次存储,多维查询,复杂关系建模。

六、真正的高手,还会用 WeakMap / WeakSet

因为它们能自动 GC 回收数据。 适用于:

  • 存储类/对象的私有数据
  • 存储 DOM 元素相关的信息
  • 避免内存泄漏
js
const privateData = new WeakMap(); class Person { constructor(name) { privateData.set(this, { name }); } get name() { return privateData.get(this).name; } }

对象销毁后私有数据自动清理,不需要手动管理。

七、总结:Map / Set 为什么面试大明星?

因为它们解决了 JS 最根本的结构问题:

维度Object/ArrayMap/Set
Key 类型有限制任意类型
唯一性手动维护Set 自动保证
顺序性不稳定保序
查找性能O(n)O(1)
增删效率
表意性
适合建模吗一般完美

面试官真正想听的不是 API,而是:

“你能否在大型系统里,用对数据结构。”

懂 Map / Set 的前端,是可以把数据结构写进架构的人。

最后:一句话送给你

Map 和 Set 不是为了让代码更‘炫’,而是为了让代码更‘对’。 用合适的数据结构,是工程师从初级走向高级的关键分水岭。

周温

周温

全栈开发工程师,专注于前端技术栈和物联网应用开发。拥有丰富的 React、Next.js、Nest.js 项目经验,擅长构建高性能、可扩展的 Web 应用。热爱技术分享,致力于将复杂的技术问题转化为易懂的实践指南。

评论 (0)

请先登录后再发表评论

登录

还没有评论,来发表第一条吧!