前端八股JS---Map / Set / WeakMap / WeakSet

目录

一、核心区别速览

二、Map(键值对集合)

[2.1 是什么](#2.1 是什么)

[2.2 与普通对象的区别](#2.2 与普通对象的区别)

[2.3 常用方法](#2.3 常用方法)

[2.4 遍历方式](#2.4 遍历方式)

[2.5 使用场景](#2.5 使用场景)

三、Set(不重复值的集合)

[3.1 是什么](#3.1 是什么)

[3.2 常用方法](#3.2 常用方法)

[3.3 遍历方式](#3.3 遍历方式)

[3.4 使用场景](#3.4 使用场景)

[四、WeakMap(弱引用 Map)](#四、WeakMap(弱引用 Map))

[4.1 是什么](#4.1 是什么)

[4.2 核心特点](#4.2 核心特点)

[4.3 常用方法](#4.3 常用方法)

[4.4 与 Map 的区别](#4.4 与 Map 的区别)

[4.5 使用场景](#4.5 使用场景)

[五、WeakSet(弱引用 Set)](#五、WeakSet(弱引用 Set))

[5.1 是什么](#5.1 是什么)

[5.2 核心特点](#5.2 核心特点)

[5.3 常用方法](#5.3 常用方法)

[5.4 使用场景](#5.4 使用场景)

六、面试高频问题

[Q1:Map 和 Object 的区别?](#Q1:Map 和 Object 的区别?)

[Q2:Set 如何实现数组去重?](#Q2:Set 如何实现数组去重?)

[Q3:WeakMap 和 Map 的区别?什么时候用 WeakMap?](#Q3:WeakMap 和 Map 的区别?什么时候用 WeakMap?)

[Q4:为什么 WeakMap 不可遍历?](#Q4:为什么 WeakMap 不可遍历?)

[Q5:[...arr] 和 arr 有什么区别?](#Q5:[...arr] 和 arr 有什么区别?)

[Q2:展开运算符和 Object.assign() 有什么区别?](#Q2:展开运算符和 Object.assign() 有什么区别?)

[Q3:... 和 rest 参数有什么区别?](#Q3:... 和 rest 参数有什么区别?)


一、核心区别速览

对比 Map Set WeakMap WeakSet
存储内容 键值对 不重复的值 键值对(键只能是对象) 不重复的对象
键的类型 任意类型 只有值(无键) 只能是对象 只能是对象
是否可遍历 ✅ 是 ✅ 是 ❌ 否 ❌ 否
有 size 属性 ✅ 有 ✅ 有 ❌ 无 ❌ 无
弱引用 ❌ 否 ❌ 否 ✅ 是 ✅ 是
GC 不影响 ❌ 会阻止回收 ❌ 会阻止回收 ✅ 不阻止回收 ✅ 不阻止回收

二、Map(键值对集合)

2.1 是什么

键值对集合,可以理解为"更强大的对象"。

2.2 与普通对象的区别

对比 Object Map
键的类型 只能是 String / Symbol 任意类型(对象、数组、函数、NaN)
顺序 不严格保证 保留插入顺序
长度 Object.keys(obj).length map.size
遍历 for...in + hasOwnProperty 直接 for...of
性能 一般 频繁增删时更好

2.3 常用方法

javascript 复制代码
const map = new Map()

// 设置
map.set('name', '张三')
map.set(123, '数字键')
map.set({ id: 1 }, '对象键')

// 获取
map.get('name')      // '张三'
map.get(123)         // '数字键'

// 判断
map.has('name')      // true

// 删除
map.delete('name')   // true

// 长度
map.size             // 2

// 清空
map.clear()

2.4 遍历方式

javascript 复制代码
const map = new Map([
  ['name', '张三'],
  ['age', 18]
])

// for...of
for (let [key, value] of map) {
  console.log(key, value)
}

// forEach
map.forEach((value, key) => {
  console.log(key, value)
})

// 只遍历键
for (let key of map.keys()) { }

// 只遍历值
for (let value of map.values()) { }

2.5 使用场景

场景 说明
需要非字符串作为键 用对象作为键
频繁增删键值对 Map 性能更好
需要有序遍历 Map 保留插入顺序

三、Set(不重复值的集合)

3.1 是什么

一组不重复的值的集合,只有值,没有键。

3.2 常用方法

javascript 复制代码
const set = new Set()

// 添加(重复添加无效)
set.add(1)
set.add(2)
set.add(2)   // 无效,不会重复
set.add(3)

// 判断
set.has(2)   // true

// 删除
set.delete(2)

// 长度
set.size     // 2

// 清空
set.clear()

3.3 遍历方式

javascript 复制代码
const set = new Set([1, 2, 3])

// for...of
for (let value of set) {
  console.log(value)
}

// forEach
set.forEach(value => {
  console.log(value)
})

3.4 使用场景

场景 代码
数组去重 [...new Set(arr)]
判断值是否存在 set.has(value)
存储唯一 ID 防止重复添加
javascript 复制代码
// 数组去重(最经典)
const arr = [1, 2, 2, 3, 3, 3]
const unique = [...new Set(arr)]  // [1, 2, 3]

// 字符串去重
const str = 'hello'
const uniqueStr = [...new Set(str)].join('')  // 'helo'
  • Set 可以接收一个可迭代对象(数组、字符串等)
  • 字符串 'hello' 是一个可迭代对象,会按字符迭代

  • Set 自动去重,所以 'l' 只保留一个,得到

    复制代码
    {'h', 'e', 'l', 'o'}
  • ... 可以把一个"可迭代对象"(数组、字符串、Set、Map 等)拆成一个个独立的值。

  • Set 是有序的可迭代对象,按插入顺序展开

  • 得到一个新数组

    复制代码
    ['h', 'e', 'l', 'o']
  • join('') 把数组元素用空字符串连接

  • ['h', 'e', 'l', 'o']'helo'


四、WeakMap(弱引用 Map)

4.1 是什么

弱引用版本的 Map,键必须是对象,不会阻止垃圾回收。

4.2 核心特点

特点 说明
键只能是对象 不能用基本类型作为键
弱引用 键对象没有其他引用时,会被 GC 回收
不可遍历 没有 size、没有 clear()、不能 for...of
不会内存泄漏 键被回收后,对应的值也会被自动删除

4.3 常用方法

javascript 复制代码
const weakMap = new WeakMap()

let obj = { id: 1 }

weakMap.set(obj, '附加数据')
weakMap.get(obj)    // '附加数据'
weakMap.has(obj)    // true
weakMap.delete(obj) // true

// 当 obj = null 时,这个键值对会被自动垃圾回收
obj = null
// weakMap 中的这个条目会自动消失

4.4 与 Map 的区别

对比 Map WeakMap
键的类型 任意类型 只能是对象
弱引用
可遍历
有 size
内存泄漏风险 有(需要手动清理) 无(自动回收)

4.5 使用场景

场景 说明
给 DOM 元素附加数据 DOM 删除后,数据自动回收
缓存 不影响对象回收
存储私有数据 防止内存泄漏
javascript 复制代码
// 给 DOM 元素附加数据
const weakMap = new WeakMap()

const div = document.getElementById('app')
weakMap.set(div, { clicks: 0 })

div.addEventListener('click', () => {
  const data = weakMap.get(div)
  data.clicks++
})

// 当 div 被移除后,weakMap 中的数据会自动回收

五、WeakSet(弱引用 Set)

5.1 是什么

弱引用版本的 Set,只能存储对象,不能存储基本类型。

5.2 核心特点

特点 说明
只能存对象 不能存数字、字符串等基本类型
弱引用 对象被回收时,Set 中自动移除
不可遍历 没有 size、不能 for...of

5.3 常用方法

javascript 复制代码
const weakSet = new WeakSet()

let obj1 = { id: 1 }
let obj2 = { id: 2 }

weakSet.add(obj1)
weakSet.add(obj2)

weakSet.has(obj1)   // true
weakSet.delete(obj1) // true

// obj2 被回收后,weakSet 中自动移除
obj2 = null

5.4 使用场景

场景 说明
标记对象是否已处理 防止重复处理
存储临时对象 不影响垃圾回收
javascript 复制代码
// 标记对象是否已处理
const processed = new WeakSet()

function process(item) {
  if (processed.has(item)) return
  // 处理逻辑...
  processed.add(item)
}

六、面试高频问题

Q1:Map 和 Object 的区别?

答:

  1. 键的类型:Object 只能是 String/Symbol,Map 可以是任意类型

  2. 顺序:Map 保留插入顺序,Object 不严格保证

  3. 长度:Map 有 size 属性,Object 需要 Object.keys().length

  4. 遍历:Map 可直接 for...of,Object 需要先获取 keys

Q2:Set 如何实现数组去重?

答:

[...new Set(arr)]

Q3:WeakMap 和 Map 的区别?什么时候用 WeakMap?

答:

  • 区别:WeakMap 键只能是对象,弱引用,不可遍历,没有 size

  • 使用场景:给 DOM 元素附加数据、缓存、存储私有数据,避免内存泄漏

Q4:为什么 WeakMap 不可遍历?

答:

因为 WeakMap 是弱引用,键对象可能随时被垃圾回收。如果可遍历,遍历过程中键被回收,会造成不确定性。所以设计成不可遍历。

Q5:[...arr]arr 有什么区别?

javascript 复制代码
const arr1 = [1, 2, 3]
const arr2 = [...arr1]

arr1 === arr2  // false(不是同一个数组)
arr1[0] === arr2[0]  // true(里面的元素是同一个)

Q2:展开运算符和 Object.assign() 有什么区别?

javascript 复制代码
// 对象复制
const obj = { a: 1, b: 2 }

// 方式1:展开运算符
const copy1 = { ...obj }

// 方式2:Object.assign
const copy2 = Object.assign({}, obj)

// 效果一样,都是浅拷贝

Q3:...rest 参数有什么区别?

javascript 复制代码
// 展开运算符:数组/对象 → 拆开
const arr = [1, 2, 3]
console.log(...arr)  // 1 2 3(拆开)

// rest 参数:多个值 → 收拢成数组
function sum(...args) {  // rest 参数,收拢
  return args.reduce((a, b) => a + b, 0)
}
sum(1, 2, 3)  // 6
相关推荐
我滴老baby2 小时前
工具调用全景解析从Function Calling到MCP协议的完整实践
开发语言·人工智能·python·架构·fastapi
feifeigo1232 小时前
自适应大邻域搜索(ALNS)算法的MATLAB 实现
开发语言·算法·matlab
冴羽3 小时前
3 招让你的 Shadcn 出海应用性能提升 40 倍
前端·javascript·next.js
沐知全栈开发3 小时前
API 类别 - 实用工具
开发语言
中议视控3 小时前
网络中控系统通过推流软件实现可视化:RTSP,H265,WEB等推流
前端·网络
Hsuna3 小时前
Tailwind CSS 比起传统CSS框架无法实现的一些功能
前端·react.js
Cx330❀3 小时前
Qt 入门指南:从零搭建开发环境到第一个图形界面程序
xml·大数据·开发语言·网络·c++·人工智能·qt
SilentSamsara3 小时前
装饰器基础:从闭包到装饰器的自然演变
开发语言·前端·vscode·python·青少年编程·pycharm
咸鱼翻身更入味3 小时前
Agent流式输送
前端