ES6 Set和Map用法详解(附实例+避坑指南)
在ES6之前,JavaScript中用于存储数据的主要结构是对象(Object)和数组(Array),但它们在某些场景下存在明显局限性:比如对象的键只能是字符串或Symbol,数组无法快速去重,查找元素效率较低。为了解决这些问题,ES6引入了两种新的数据结构——Set和Map,它们分别对应“无重复值的集合”和“键值对映射”,在实际开发中(尤其是前端框架、数据处理场景)应用广泛。本文将详细讲解Set和Map的核心用法、实例场景及常见误区,帮助大家快速上手。
一、Set 用法详解
1.1 什么是Set?
Set是一种无序、唯一的集合,它里面的每个元素都是唯一的,没有重复值。Set本身是一个构造函数,用来生成Set实例。
核心特点:
元素不重复,自动去重
无序(不保证插入顺序与遍历顺序一致,与Object不同,Object在ES6后虽有顺序,但本质是“伪有序”)
可以存储任意类型的值(包括基本类型和引用类型)
没有索引,无法通过下标访问元素
1.2 Set 基本用法(创建+增删改查)
1.2.1 创建Set实例
可以通过两种方式创建Set:空实例,或传入可迭代对象(数组、字符串等)初始化。
// 1. 创建空
Set const s1 = new Set();
// 2. 传入数组初始化(自动去重)
const s2 = new Set([1, 2, 2, 3, 3, 3]);
console.log(s2); // Set(3) {1, 2, 3}
// 3. 传入字符串初始化(自动去重)
const s3 = new Set('hello');
console.log(s3); // Set(4) {'h', 'e', 'l', 'o'}(l重复被去重)
// 4. 传入其他可迭代对象(如Map、Set本身)
const s4 = new Set(s2); console.log(s4); // Set(3) {1, 2, 3}(复制一个Set)
1.2.2 核心方法(增删改查)
Set的方法较少,常用的只有6个,记住即可灵活使用:
方法 | 作用 | 示例 |
|---|---|---|
add(value) | 添加元素,返回Set本身(可链式调用) | s1.add(1).add(2) |
delete(value) | 删除指定元素,返回布尔值(成功true,失败false) | s2.delete(2) // true |
has(value) | 判断元素是否存在,返回布尔值 | s2.has(3) // true |
clear() | 清空所有元素,无返回值 | s2.clear() |
size | 属性,获取Set的元素个数(类似数组length) | console.log(s2.size) // 3 |
forEach() | 遍历Set,回调函数接收(value, key, Set本身) | s2.forEach((val) => console.log(val)) |
1.2.3 注意事项(避坑点)
Set的“去重”是基于“严格相等(===)”判断的,有两个特殊情况:
NaN和NaN被认为是相等的(虽然NaN === NaN为false,但Set中会去重)
undefined和undefined相等,null和null相等
引用类型(对象、数组)即使内容相同,也会被认为是不同元素(因为引用地址不同)
const s = new Set();
s.add(NaN); s.add(NaN);
console.log(s); // Set(1) {NaN}(去重成功)
s.add({a: 1}); s.add({a: 1});
console.log(s); // Set(2) {{a:1}, {a:1}}(去重失败,引用地址不同)
1.3 Set 实际应用场景
Set的核心优势是“自动去重”和“快速查找”,常见应用场景:
数组去重(最常用)
const arr = [1, 2, 2, 3, 4, 4, 5];const uniqueArr = [...new Set(arr)]; // [1,2,3,4,5]// 或 Array.from(new Set(arr))判断元素是否存在(效率比数组indexOf高,尤其是数据量大时)
存储不重复的标识(如用户ID、商品ID)
数组交集、并集、差集
const a = new Set([1, 2, 3]);const b = new Set([2, 3, 4]);// 并集const union = new Set([...a, ...b]); // {1,2,3,4}// 交集const intersection = new Set([...a].filter(x => b.has(x))); // {2,3}// 差集(a - b)const difference = new Set([...a].filter(x => !b.has(x))); // {1}
二、Map 用法详解
2.1 什么是Map?
Map是一种有序的键值对集合,它的键可以是任意类型(包括基本类型、引用类型),这解决了Object键只能是字符串/Symbol的局限性。Map的键是唯一的,值可以重复。
核心特点:
键可以是任意类型(字符串、数字、对象、Symbol等)
有序(插入顺序与遍历顺序一致,这是与Object的核心区别之一)
键唯一,值可以重复
可以通过键快速查找值,效率比Object高(尤其是频繁增删改查场景)
2.2 Map 基本用法(创建+增删改查)
2.2.1 创建Map实例
Map可以通过空实例创建,或传入“二维数组”(每一项是[key, value])初始化,也可以传入其他Map实例复制。
// 1. 创建空Map
const m1 = new Map();
// 2. 传入二维数组初始化
const m2 = new Map([ ['name', '张三'], [18, '年龄'], [{a: 1}, '对象键'], [Symbol('id'), 1001] ]);
console.log(m2); // Map(4) {'name' => '张三', 18 => '年龄', {a:1} => '对象键', Symbol(id) => 1001}
// 3. 复制Map
const m3 = new Map(m2);
console.log(m3); // 与m2内容一致
2.2.2 核心方法(增删改查)
Map的常用方法比Set多一些,重点记住以下7个:
方法/属性 | 作用 | 示例 |
|---|---|---|
set(key, value) | 添加/修改键值对(键存在则修改值,不存在则添加),返回Map本身(可链式调用) | m1.set('name', '李四').set('age', 20) |
get(key) | 获取指定键的值,键不存在返回undefined | m2.get('name') // '张三' |
delete(key) | 删除指定键值对,返回布尔值(成功true,失败false) | m2.delete(18) // true |
has(key) | 判断键是否存在,返回布尔值 | m2.has({a:1}) // false(引用地址不同) |
clear() | 清空所有键值对,无返回值 | m2.clear() |
size | 属性,获取Map的键值对个数 | console.log(m2.size) // 4 |
forEach() | 遍历Map,回调函数接收(value, key, Map本身) | m2.forEach((val, key) => console.log(key, val)) |
2.2.3 注意事项(避坑点)
1. Map的键唯一性判断,与Set一致,基于“严格相等(===)”,但有两个特殊情况:
NaN和NaN视为同一个键
引用类型的键,即使内容相同,只要引用地址不同,就是不同的键
2. Map与Object的区别(高频面试题):
键的类型:Map键可以是任意类型,Object键只能是字符串/Symbol
顺序:Map有序,Object(ES6前无序,ES6后伪有序)
遍历:Map可直接遍历,Object需要手动遍历(如for...in)
大小:Map可通过size属性获取,Object需要手动计算(Object.keys(obj).length)
性能:Map在频繁增删改查场景下,性能优于Object
const m = new Map();
const obj1 = {a: 1};
const obj2 = {a: 1};
m.set(obj1, 'test1'); m.set(obj2, 'test2');
console.log(m.get(obj1));
// 'test1' console.log(m.get(obj2)); // 'test2'(两个对象键不同)
2.3 Map 实际应用场景
Map的核心优势是“键可以是任意类型”和“有序性”,常见应用场景:
存储复杂键值对(如用对象作为键,关联对应的数据)
// 示例:用DOM元素作为键,存储对应的状态const btn = document.querySelector('button');const stateMap = new Map();stateMap.set(btn, {disabled: false, text: '点击'});// 后续获取按钮状态console.log(stateMap.get(btn).disabled); // false需要保持键值对顺序的场景(如表单字段、配置项)
频繁增删改查的场景(如购物车数据、用户会话信息)
替代Object存储多类型键的场景(避免键被自动转为字符串)
三、Set和Map的区别总结
很多开发者会混淆Set和Map,这里用一张表格清晰区分,方便记忆:
对比维度 | Set | Map |
|---|---|---|
数据结构 | 无重复值的集合(只有值,无键) | 键值对集合(键-值映射) |
核心作用 | 去重、判断元素存在 | 存储键值对、通过键快速查找值 |
元素特点 | 值唯一,无序 | 键唯一,值可重复,有序 |
常用方法 | add、delete、has、clear | set、get、delete、has、clear |
遍历方式 | forEach、for...of | forEach、for...of(可遍历键、值、键值对) |
四、总结
Set和Map是ES6中非常实用的数据结构,它们弥补了Object和Array的不足,在实际开发中可以根据场景灵活选择:
如果需要去重、判断元素是否存在,用Set(如数组去重、存储唯一标识);
如果需要存储键值对、用非字符串作为键,用Map(如复杂数据映射、保持键值对顺序)。
掌握它们的基本用法和避坑点,能让我们的代码更简洁、高效,尤其是在处理大量数据、复杂映射场景时,能显著提升开发效率。建议大家动手实践文中的实例,加深理解~
最后,如果你觉得本文对你有帮助,欢迎点赞、收藏,关注我,后续分享更多ES6实用技巧!
