9. 你是怎么理解ES6新增Set、Map两种数据结构的?

张开发
2026/5/17 22:38:02 15 分钟阅读
9. 你是怎么理解ES6新增Set、Map两种数据结构的?
一、面试开场怎么答更有层次如果面试官问你怎么理解 ES6 新增的 Set 和 Map不要直接说Set 是不重复的数组Map 是键值对。更好的开场方式是从它们解决了什么问题说起ES6 新增 Set 和 Map是对 JavaScript 原有数据结构的重要补充。原来我们只有数组和对象但数组不能天然去重对象的键只能是字符串或 Symbol。Set 解决了需要存储不重复值的问题Map 解决了需要任意类型作键的键值对的问题。它们让 JavaScript 的数据结构能力更完整在特定场景下性能和语义都比数组、对象更合适。这个开场就很有深度。二、Set 是什么1本质Set 是一种值的集合其中每个值都是唯一的不允许重复。const s new Set([1, 2, 3, 2, 1]) console.log(s) // Set(3) { 1, 2, 3 }2基本操作const s new Set() s.add(1) // 添加 s.add(2) s.add(2) // 重复添加无效 s.has(1) // true判断是否存在 s.delete(1) // 删除 s.size // 获取数量 s.clear() // 清空3遍历const s new Set([1, 2, 3]) for (const value of s) { console.log(value) } s.forEach(value { console.log(value) }) // 转为数组 const arr [...s] const arr2 Array.from(s)4判断唯一性的规则Set 使用的是类似严格相等的比较SameValueZero有几个特殊情况const s new Set() s.add(NaN) s.add(NaN) console.log(s.size) // 1NaN 被认为相等 s.add(0) s.add(-0) console.log(s.size) // 10 和 -0 被认为相等 s.add({}) s.add({}) console.log(s.size) // 2对象是引用比较地址不同就不重复面试加分点Set 判断唯一性用的是 SameValueZero 算法和基本一致但有两个不同NaN NaN是false但 Set 认为两个 NaN 是相同的0 -0是trueSet 也认为它们相同。5Set 的常见使用场景数组去重const arr [1, 2, 2, 3, 3, 4] const unique [...new Set(arr)] console.log(unique) // [1, 2, 3, 4]这是最常见的用法面试必须提。求并集、交集、差集const a new Set([1, 2, 3]) const b new Set([2, 3, 4]) // 并集 const union new Set([...a, ...b]) // Set { 1, 2, 3, 4 } // 交集 const intersection new Set([...a].filter(x b.has(x))) // Set { 2, 3 } // 差集a 中有b 中没有 const difference new Set([...a].filter(x !b.has(x))) // Set { 1 }快速判断是否存在const validTags new Set([div, span, p]) function isValidTag(tag) { return validTags.has(tag) }比数组的includes性能更好Set 的has是 O(1) 查找。过滤重复事件或请求比如防止同一个资源被重复加载const loaded new Set() function loadScript(url) { if (loaded.has(url)) return loaded.add(url) // 加载脚本 }三、Map 是什么1本质Map 是一种键值对集合和对象类似但键可以是任意类型不限于字符串或 Symbol。const m new Map() m.set(name, Tom) m.set(1, number key) m.set(true, boolean key) m.set({ id: 1 }, object key) console.log(m.size) // 42基本操作const m new Map() m.set(key, value) // 设置 m.get(key) // 获取 m.has(key) // 判断是否存在 m.delete(key) // 删除 m.size // 数量 m.clear() // 清空3初始化const m new Map([ [name, Tom], [age, 18], [city, Beijing] ]) console.log(m.get(name)) // Tom4遍历const m new Map([[a, 1], [b, 2]]) // 遍历键值对 for (const [key, value] of m) { console.log(key, value) } // 遍历键 for (const key of m.keys()) { console.log(key) } // 遍历值 for (const value of m.values()) { console.log(value) } // forEach m.forEach((value, key) { console.log(key, value) })注意Map 的forEach回调参数顺序是(value, key)和对象的习惯稍有不同面试里可以顺带提一下。5Map 的使用场景需要非字符串键的键值对const domMap new Map() const btn document.getElementById(btn) domMap.set(btn, { clickCount: 0 }) domMap.get(btn).clickCount用 DOM 元素作键对象无法做到Map 可以。需要频繁增删键值对Map 的增删操作性能比普通对象更好特别是在键数量大的情况下。需要保留插入顺序Map 的迭代顺序就是插入顺序对象虽然大部分情况也是但不是规范保证的。const m new Map() m.set(c, 3) m.set(a, 1) m.set(b, 2) console.log([...m.keys()]) // [c, a, b]缓存 / 备忘录const cache new Map() function compute(n) { if (cache.has(n)) return cache.get(n) const result n * n cache.set(n, result) return result }统计频率const words [apple, banana, apple, cherry, banana, apple] const freq new Map() for (const word of words) { freq.set(word, (freq.get(word) || 0) 1) } console.log(freq) // Map { apple 3, banana 2, cherry 1 }四、Set 和 Map 的对比对比SetMap存储内容只有值没有键键值对键的类型无键任意类型是否去重值唯一键唯一主要场景去重、集合运算键值存储、缓存遍历顺序插入顺序插入顺序五、Map 和 Object 的对比这是面试高频对比题。对比维度MapObject键的类型任意类型字符串 / Symbol键值对数量size直接获取需要手动计算迭代可直接迭代需要Object.keys()等插入顺序保证按插入顺序大部分情况保证但非规范性能频繁增删更好相对较差原型链没有继承属性有原型链可能有意外属性序列化不支持JSON.stringify支持面试加分说法如果只是存储简单的字符串键值对或者需要 JSON 序列化用普通对象更合适。如果需要非字符串键、需要频繁增删、或者需要保证迭代顺序Map 更合适。六、WeakSet 和 WeakMap这部分不要漏掉面试常追问。WeakSet只能存储对象不能存基本类型对对象的引用是弱引用不会阻止垃圾回收不可遍历没有sizeconst ws new WeakSet() let obj { name: Tom } ws.add(obj) obj null // obj 可以被垃圾回收WeakSet 不会阻止使用场景标记对象是否处理过防止内存泄漏WeakMap键只能是对象键是弱引用键对象被回收后对应的键值对自动消失不可遍历const wm new WeakMap() let dom document.getElementById(btn) wm.set(dom, { data: some data }) dom null // dom 被回收wm 中对应的条目也会消失使用场景给 DOM 元素附加数据避免内存泄漏私有属性存储缓存对象被回收后缓存自动清除面试加分说法WeakMap 和 WeakSet 最核心的特点是弱引用这意味着它们存储的对象不会因为被集合引用而无法被垃圾回收。这在需要给对象附加数据但又不想影响对象生命周期的场景下非常有用比如给 DOM 节点附加事件数据。七、面试标准回答ES6 新增的 Set 和 Map是对 JavaScript 原有数据结构的重要补充。Set 是一种值的集合核心特点是值唯一不允许重复。它的判断唯一性用的是 SameValueZero 算法和严格相等类似但 NaN 在 Set 中被认为是相等的。Set 最常用的场景是数组去重、集合运算并集、交集、差集以及需要 O(1) 时间复杂度判断值是否存在的场景。Map 是一种键值对集合核心特点是键可以是任意类型不限于字符串或 Symbol。相比普通对象Map 的优势在于键类型不受限、size可以直接获取、迭代顺序有规范保证、频繁增删性能更好、没有原型链干扰。Map 常用于需要非字符串键的存储、缓存、频率统计、需要保证迭代顺序的场景。另外ES6 还提供了 WeakSet 和 WeakMap它们对存储的对象是弱引用不会阻止垃圾回收。WeakMap 的典型场景是给 DOM 元素附加数据而不造成内存泄漏也常用于存储私有属性。总体上Set 和 Map 让 JavaScript 的数据结构更加完整在特定场景下比数组和对象更语义清晰、性能更好。八、精简版面试回答Set 是值唯一的集合核心用途是数组去重和集合运算has方法是 O(1) 查找。Map 是键可以是任意类型的键值对比普通对象更灵活支持直接获取size、迭代顺序有保证、频繁增删性能更好。WeakSet 和 WeakMap 是弱引用版本不阻止垃圾回收适合给对象附加数据而不造成内存泄漏。选择上简单字符串键值对用对象需要去重用 Set需要非字符串键或频繁增删用 Map。九、如果想答得更高级可以补这几句1Set 和 Map 都实现了可迭代协议Set 和 Map 都内置了[Symbol.iterator]可以直接用for...of遍历也可以用扩展运算符展开这和普通对象不同。2Map 可以和 Object 互转// Object 转 Map const obj { a: 1, b: 2 } const map new Map(Object.entries(obj)) // Map 转 Object const obj2 Object.fromEntries(map)3Map 不支持 JSON 序列化需要处理const m new Map([[a, 1]]) // 直接序列化会丢失数据 JSON.stringify(m) // {} // 需要先转换 JSON.stringify(Object.fromEntries(m)) // {a:1}这是实际开发中容易踩的坑主动提会加分。十、一句话总结面试官真正想听的是你是否知道Set 和 Map 分别解决了什么问题你能不能说清楚Set 的唯一性判断规则你是否理解Map 和 Object 的核心区别你能不能结合去重、集合运算、缓存、频率统计等实际场景来讲你有没有了解WeakSet 和 WeakMap 的弱引用特性和使用场景

更多文章