ES6 Set与WeakSet集合详解:API、遍历与内存管理
本文深入解析ES6中的Set与WeakSet数据结构。详细讲解Set的创建、API使用(add/delete/clear)、基于Object.is的唯一值判断逻辑及三种遍历方式。通过案例演示数组去重、交集与差集计算。同时剖析WeakSet的弱引用特性,说明其仅支持对象引用、不可遍历及自动垃圾回收机制,帮助开发者合理选择集合类型。
Set是一种特殊的集合数据结构,其核心特性是存储的值永不重复。
在日常开发中,我们频繁使用数组,但常需担心插入重复值的问题。Set集合提供了专门的数据结构,确保插入的值保持唯一性,无需手动去重。
Set集合的基础API操作。
可以通过Set.prototype.constructor构造函数来创建Set实例。
/*
* 仅实例化:调用构造函数,不传参数
*/
let empty_set = new Set()
/*
* 实例化同时初始化:传入任意iterate对象,将其转换成Set集合
*/
let transfer_set4arr = new Set([1, 2, 3])
// 返回Set(3) {1, 2, 3}
let transfer_set4string = new Set("huilin")
// 返回Set(5) {"h", "u", "i", "l", "n"}
let transfer_set4set = new Set(new Set([5, 6]))
// 返回Set(2) { 5, 6 }
访问Set.prototype.size属性,可以获取集合中当前包含的元素个数。
console.log(empty_set.size) // 0
console.log(transfer_set4arr.size) // 3
调用Set.prototype.has(value)方法,用于判断指定元素是否存在于集合中。
// 相比起Array.includes(),Set.has()性能更高,因为专门对成员测试进行了优化
console.log(empty_set.has(1)) // false
console.log(transfer_set4arr.has('h')) // true
关于唯一值的判断机制。
- Set集合为确保值的唯一性,内部使用Object.is(value1, value2)进行判断,而非通过===(恒等符号)进行判断。这是因为恒等判断会对两边变量进行强制类型转换,可能导致不符合预期的结果。
- 例如,两个变量的值均为NaN,或者0和-0,在JavaScript中使用===判断是不相等的,但Object.is()认为它们是同一个值。因此,这两个值不能同时存入Set集合中,后者会覆盖前者或被视为重复。
想了解更多关于Object.is()的具体行为,请跳转查看:developer.mozilla.org/zh-CN/docs/…
let n1 = NaN
let n2 = NaN
console.log(n1 === n2)
// 恒等符号判断两者不一致,输出false
console.log(Object.is(n1,n2))
// 但Object.is()判断两者是相同的,输出false
// Set集合不允许将两个NaN放入集合
let set = new Set()
set.add(n1).add(n2)
console.log(set.size)
// size: 1
而面对复杂数据类型(如对象)时,Set主要通过对象的引用地址进行判断。如果引用地址不一致,即便两个对象的数据结构完全一致,Set也认为它们是不同的值,因此都能成功存入集合。
let same_value_set = new Set();
// 先存入一个对象
same_value_set.add({num: 0});
// 再存入一个结构一致的新对象
let obj = {num: 0};
same_value_set.add(obj);
// 都能存入成功
console.log(same_value_set.size); // 2
调用Set.prototype.add(value)方法,可以向集合中追加数据。
// add()方法可以追加任意类型的数据,不论是原始值或者是对象引用
let set1 = new Set()
// 由于add()方法始终返回当前实例的引用,所以进行链式调用
set1.add(1).add(2).add(3)
console.log(set1) // Set(3) {1, 2, 3}
// 注意:当add()传入数组时,Set会将数组实例插入集合,而不是数组内的元素
set1.add([4, 5])
console.log(set1) // Set(4) {1, 2, 3, [4, 5]}
调用Set.prototype.delete(value)方法,可以从集合中移除指定的元素。
// delete()方法返回移除操作是否成功,与.has()方法一样
let success = set1.delete(1)
console.log(success)
// true
调用Set.prototype.clear()方法,可以清空集合中的所有元素。
let num_set = new Set([1, 6, 3])
console.log(num_set)
// Set(3) { 1, 6, 3 }
set1.clear()
console.log(num_set)
// Set(0) {}
Set集合的3种遍历方式。
虽然集合没有下标或索引,通常被视为“无序集合”,但JavaScript引擎会记住元素插入的顺序。因此,在遍历集合时,元素会按照插入的顺序进行迭代输出。
可以直接使用for...of循环遍历Set集合。
let set = new Set([1, 2, 3, 4, 5])
for(let item of set){
console.log(item)
}
// 依次输出:1 2 3 4 5
也可以创建迭代器进行遍历,包括entries()、keys()和values()方法。
/*
* 创建迭代器的有三种方式
* Set.prototype.entries()
* Set.prototype.keys()
* Set.prototype.values()
*/
// Set集合只有value而没有key,但为了使得和遍历Map对象相似,Set.entries()创建新的Iterator对象时,每一项的键和值都相等,即[value,value]
for(let [key,value] of set.entries()){
console.log(value)
}
// 依次输出:1 2 3 4 5
// Set.keys()创建新的Iterator对象,返回每一项值
for(let key of set.keys()){
console.log(key)
}
// 依次输出:1 2 3 4 5
// Set.values()和Set.keys()一致,返回每一项的值
for(let value of set.values()){
console.log(value)
}
// 依次输出:1 2 3 4 5
调用Set.prototype.forEach(callbackFn)方法,可以按照插入顺序遍历并执行回调函数。
// forEach(callbackFn) 按照插入顺序调用callbackFn,取出每项值
set.forEach(item => {
console.log(item)
})
// 依次输出:1 2 3 4 5
Set集合的实际案例实践。
Set集合与Array数组之间的相互转换技巧。
/*
* Set转Array
*/
let set1 = new Set([1, 2, 3])
// Array.from()方法
let arr1 = Array.from(set1)
// 扩展运算符
let arr2 = [...set1]
/*
* Array转Set
*/
// 利用Set构造函数
let set = new Set(array)
单个数组去重:利用Set的特性去除数组中的重复元素。
let set = new Set([1, 2, 4, 4, 2, 5])
console.log(set)
// Set(4) { 1, 2, 4, 5 }
多个数组合并去重:将多个数组合并为一个数组后,利用Set去除重复项。
let arr1 = [1, 2, 4]
let arr2 = [1, 5, 6]
// 利用Set集合的特性,集合内的元素都是唯一的
let result = new Set([...set1, ...set2])
console.log(result)
// Set(5) { 1, 2, 4, 5, 6 }
获取交集(重复的元素):找出两个集合中共有的元素。
let set1 = new Set([1, 2, 4])
let set2 = new Set([1, 5, 6])
// 返回set1和set2都存在的元素
let result = new Set([...set1].filter(x => set2.has(x)))
console.log(result)
// Set(1) { 1 }
判断是否有交集(重复的元素):检查两个集合是否存在共同元素。
let set1 = new Set([1, 2, 4])
let set2 = new Set([1, 5, 6])
function isMixed(set, subset) {
for (let elem of subset) {
if (set.has(elem)) {
return true;
}
}
return false;
}
console.log(isMixed(set1, set2))
// true
获取差集:返回存在于一个集合但不在另一个集合中的元素。
let set1 = new Set([1, 2, 4])
let set2 = new Set([1, 5, 6])
function difference(setA, setB) {
let result = new Set()
for (let elem of setB) {
if(setA.has(elem)){
result.add(elem)
}
}
return result;
}
console.log(difference(set1, set2))
WeakSet“弱”在哪里?
除了Set集合外,ES6还提供了WeakSet和WeakMap。既然集合的名字都叫“Weak(弱)的集合”了,究竟它“弱”在哪里呢?
WeakSet在功能上有所限制。
WeakSet不允许插入原始值,仅支持对象的引用;
let val1 = {id: 1},
val2 = {id: 2}
let ws = new WeakSet()
// 和Set集合一样,WeakSet的值也不重复,同时add()也返回集合实例,所以可以链式操作
ws.add(val1).add(val1).add(val2)
// 不允许插入基础数据类型
ws.add(3)
// 报错:TypeError:Invalid value used in WeakSet
// 但可以先包装成对象后再插入
let val3 = new Number(3)
ws.add(val3)
console.log(ws.has(val3))
// 输出:true
- WeakSet仅实现了add()、has()、delete()三个操作方法;
- WeakSet不允许遍历,也没有size或者length属性;
弱引用机制解析。
要说弱引用,先看看什么是强引用:
// 声明一个对象
let handsome = {
name: 'huilin',
age: 30
}
// 放入数组
let arr = [1, handsome, 2]
console.log('release before arr length', arr.length) // 3
// 放入Map
let user = {
oid: 10001,
classify: 'Chinese',
staffReference: handsome
}
console.log('release before map length', Object.keys(user).length) // 3
console.log('----')
// 突然把对象置为null
handsome = null
// 强引用的容器中,对象仍然存在没有被回收
console.log('release after arr length', arr.length) // 3
console.log(arr[1]) // { name: 'huilin', age: 30 }
console.log('release after map length', Object.keys(user).length) // 3
console.log(user.staffReference) // { name: 'huilin', age: 30 }
从测试代码看出,除非容器销毁,否则引用的对象一直没有被回收。而所谓弱引用,就是希望容器是根据元素自动伸缩的,一旦对象为null(不可达),容器中的引用也跟着被垃圾回收机制回收。
let obj1 = {
name: 'huilin',
age: 30
}
let obj2 = {
name: 'cc',
age: 29
}
let ws1 = new WeakSet()
ws1.add(obj1).add(obj2)
console.log(ws1.has(obj1)) // true
// 不管是从容器操作元素
ws1.delete(obj1)
console.log(ws1.has(obj1)) // false
// 或者是对象自己置为null,都会自动回收
obj2 = null
console.log(ws1.has(obj2)) // false
Reference相关文档。
- zh.javascript.info/map-set
- es6.ruanyifeng.com/#docs/set-m…
总结。
到此这篇关于ES6中Set与WeakSet集合的文章就介绍到这了,更多相关ES6 Set与WeakSet集合内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
