ES6引入的Set和Map是两种全新的数据结构,它们弥补了传统数组和对象在部分场景下的不足,在实际开发中有着非常广泛的应用。Set的核心特点是成员值唯一,没有重复的值,内部使用类似精确相等算法的规则判断值是否重复。Map则是键值对的集合,它的键可以是任意类型的数据,包括对象、函数等,这一点和传统对象只能使用字符串或Symbol作为键有明显区别。

Set数据结构的核心特性
Set本身是一个构造函数,用来生成Set数据结构,创建时可以直接传入可迭代对象初始化成员。Set实例有以下常用属性和方法:
- size属性:返回Set实例的成员总数
- add(value):添加某个值,返回Set结构本身
- delete(value):删除某个值,返回布尔值表示是否删除成功
- has(value):判断某个值是否为Set的成员,返回布尔值
- clear():清除所有成员,没有返回值
Set的常见应用场景
1. 数组去重
利用Set成员唯一的特性,可以快速实现数组去重,比传统的双重循环或者indexOf判断效率更高。
// 简单数组去重
const arr = [1, 2, 2, 3, 3, 4, 5, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // 输出 [1, 2, 3, 4, 5]
// 复杂数组去重,基于对象某个属性去重
const userList = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 1, name: '张三' }
];
const uniqueUserSet = new Set();
const uniqueUserList = userList.filter(item => {
if (uniqueUserSet.has(item.id)) {
return false;
} else {
uniqueUserSet.add(item.id);
return true;
}
});
console.log(uniqueUserList); // 输出 [{id:1,name:'张三'},{id:2,name:'李四'}]
2. 集合运算
Set可以很方便地实现并集、交集、差集等集合运算。
const setA = new Set([1, 2, 3, 4]); const setB = new Set([3, 4, 5, 6]); // 并集 const unionSet = new Set([...setA, ...setB]); console.log([...unionSet]); // 输出 [1, 2, 3, 4, 5, 6] // 交集 const intersectionSet = new Set([...setA].filter(item => setB.has(item))); console.log([...intersectionSet]); // 输出 [3, 4] // 差集 A - B const differenceSet = new Set([...setA].filter(item => !setB.has(item))); console.log([...differenceSet]); // 输出 [1, 2]
3. 判断元素是否存在
当我们需要判断某个值是否在集合中时,使用Set的has方法比数组的includes方法效率更高,尤其是数据量较大的时候,Set的查找时间复杂度接近O(1),而数组的includes是O(n)。
const allowList = new Set(['admin', 'editor', 'viewer']);
const userRole = 'admin';
if (allowList.has(userRole)) {
console.log('用户有操作权限');
} else {
console.log('用户无操作权限');
}
Map数据结构的核心特性
Map同样是构造函数,用来生成Map数据结构,它的键和值可以是任意类型的数据,传统对象的键只能是字符串或者Symbol。Map实例的常用属性和方法如下:
- size属性:返回Map结构的成员总数
- set(key, value):设置键名key对应的键值为value,返回整个Map结构
- get(key):读取key对应的键值,如果找不到key则返回undefined
- has(key):判断某个键是否在当前Map对象中,返回布尔值
- delete(key):删除某个键,返回布尔值表示是否删除成功
- clear():清除所有成员,没有返回值
Map的常见应用场景
1. 存储关联数据,键为任意类型
当我们需要使用对象、函数等作为键来存储关联数据时,传统对象无法满足需求,这时候使用Map就非常合适。
// 以DOM元素作为键存储相关数据
const domMap = new Map();
const btn = document.querySelector('button');
domMap.set(btn, { clickCount: 0, lastClickTime: null });
btn.addEventListener('click', () => {
const data = domMap.get(btn);
data.clickCount++;
data.lastClickTime = new Date().toLocaleString();
domMap.set(btn, data);
console.log(`按钮点击次数:${data.clickCount},最后点击时间:${data.lastClickTime}`);
});
2. 数据映射转换
当我们需要建立两个数据集合之间的映射关系时,Map比使用对象更清晰,也支持更多类型的映射键。
// 状态码到提示信息的映射
const statusMap = new Map([
[200, '请求成功'],
[404, '资源未找到'],
[500, '服务器内部错误'],
[-1, '网络异常']
]);
function getStatusMsg(code) {
return statusMap.get(code) || '未知状态码';
}
console.log(getStatusMsg(200)); // 输出 请求成功
console.log(getStatusMsg(404)); // 输出 资源未找到
3. 缓存计算结果
对于计算成本较高的函数,我们可以使用Map缓存已经计算过的结果,避免重复计算,提升性能。
// 缓存斐波那契数列计算结果
const fibCache = new Map();
function fib(n) {
if (n <= 1) return n;
if (fibCache.has(n)) {
return fibCache.get(n);
}
const result = fib(n - 1) + fib(n - 2);
fibCache.set(n, result);
return result;
}
console.log(fib(10)); // 输出 55,计算过程会缓存中间结果
console.log(fib(10)); // 直接读取缓存,无需重复计算
Set和Map的使用注意事项
在使用Set和Map的时候,需要注意以下几点:
- Set判断值是否重复时,NaN等于自身,而精确相等运算符认为NaN不等于自身
- Map的键是基于引用地址比较的,两个内容相同的对象作为键会被认为是不同的键
- 如果需要遍历Set或者Map,可以使用for...of循环,也可以使用forEach方法
- Set和Map都支持迭代器协议,可以通过keys()、values()、entries()方法获取对应的迭代器
Set和Map作为ES6新增的核心数据结构,在很多场景下都能替代传统的数组和对象,让代码更简洁、效率更高。开发者可以根据具体的需求选择合适的数据结构,提升代码的可读性和执行性能。
SetMapJavaScriptES6数据结构修改时间:2026-06-18 16:48:40