es6中Set和WeakSet的用法和区别
ES6中引入了Set
和WeakSet
两种新的数据结构,它们主要用于处理不重复值的集合。在面试中也极易被问到WeakSet()的弱引用是怎么理解的,书上的概念晦涩难懂,必须将实际结合起来理解。下面我将会层层递进说明。
Set
-
定义与用法:
Set
是一个集合类型,用于存储唯一值,无论是原始类型值(如字符串、数字)还是对象引用。- 创建一个
Set
实例可以通过new Set()
来完成,可以接受一个可迭代对象(如数组、字符串)作为参数,将其中的元素添加到Set
中。
let set = new Set([1, 2, 3, 4, 4, 3]); // set现在包含1, 2, 3, 4
-
Set 的常用方法
Set
提供了丰富的API来处理集合数据,包括:
-
add(value) : 向集合中添加一个新的元素。如果元素已存在,则不会重复添加。
``` let s = new Set(); s.add(1); ```
-
delete(value) : 如果集合中存在指定的元素,则删除它并返回
true
,否则返回false
。s.delete(1);
-
has(value) : 检查集合中是否包含指定的元素,返回
true
或false
。s.has(1);
-
clear() : 清空集合中的所有元素。
s.clear();
-
size: 只读属性,返回集合中元素的数量。
-
forEach(callback[, thisArg]) : 对集合中的每个元素执行给定的函数。
-
-
特性:
- 成员值唯一,自动去除重复项。
let numbers = [1, 2, 3, 4, 4, 3, 5]; let uniqueNumbers = new Set(numbers); console.log([...uniqueNumbers]); // 输出: [1, 2, 3, 4, 5]
- 可以进行集合操作,如并集、交集等。
const set1 = new Set([1, 2]); const set2 = new Set([2, 3]); // 求并集 const res2 = new Set([...set1, ...set2]); console.log([...res2]); // [1, 2, 3] // 求交集 const res = new Set([...set1].filter(item => set2.has(item))); //返回共有的元素 console.log([...res]); // [2]
- 支持迭代(例如,可以使用
for...of
循环遍历)。
let letters = new Set(['a', 'b', 'c']); for(let letter of letters) { console.log(letter); } // 输出: // a // b // c
WeakSet
-
定义与用法:
WeakSet
同样用于存储唯一对象,但与Set
不同的是,它只接受对象作为成员,并且这些对象是以弱引用的方式存储的。- 创建一个
WeakSet
实例也是通过new WeakSet()
,但它的构造函数不接受数组等可迭代对象作为参数,而是接受一个或多个对象作为单独的参数或使用add
方法添加。
let obj1 = {}; let weakSet = new WeakSet([obj1]);
-
WeakSet 的常用方法
由于WeakSet
的设计主要用于持有对象的弱引用,它的API相对精简:
- add(value) : 向集合中添加一个对象。如果对象已经存在于集合中,则此操作无效果。
- delete(value) : 如果集合中存在指定的对象,则将其移除并返回
true
,否则返回false
。 - has(value) : 检查集合中是否包含指定的对象,返回
true
或false
。
注意,WeakSet
没有clear
方法,也不支持遍历操作(如forEach
、不能用在for...of
循环中),因为它是不可迭代的,且没有size
属性来获取元素数量,这都是为了保持其内部对象的弱引用特性,避免影响垃圾回收。
-
特性:
- 成员必须是对象,
let obj1 = { id: 1 }; let obj2 = { id: 2 }; let weakSet = new WeakSet([obj1, obj2]); // 尝试添加非对象值会报错(注:严格模式下) weakSet.add(3); // TypeError: Invalid value used in weak set
-
是弱引用,这意味着如果对象没有其他引用指向它,垃圾回收机制会自动回收该对象,即使它在
WeakSet
中。
let obj = {}; let ws = new WeakSet([obj]); // 执行中垃圾回收是随机发生的,我们无法预判,但当一个值被赋值为null时,则表示垃圾回收在此刻发生 obj = null; // 在这一时刻,若没有其他对obj的引用,垃圾回收机制会回收obj对象, // 而不会因为ws中仍有对该对象的引用而不去回收obj对象。
- 不可迭代,没有大小属性,也不支持
forEach
等方法。
// weakSet.size; // undefined, WeakSet没有size属性 // for(let item of ws) { console.log(item); } // 这会报错,因为WeakSet不可迭代
- 由于其弱引用的特性,
WeakSet
主要用于跟踪对象,而不用担心阻止对象被垃圾回收。 - 总结:一个对象obj存在了其他的结果中,当后续只存在weakSet对它的引用,该对象的内存依然会被回收
区别总结
-
成员类型:
Set
可以包含任何类型的值(包括原始值和对象),而WeakSet
只能包含对象。 -
引用强度:
Set
对成员的引用是强引用,意味着只要Set
存在,它所包含的对象就不会被垃圾回收;而WeakSet
中的对象引用是弱引用,对象可以被垃圾回收,即使它还在WeakSet
中。 -
迭代与大小:
Set
是可以迭代的,可以用size
属性获取成员数量;而WeakSet
既不可迭代,也没有size
属性。 -
用途:
Set
适用于需要维护唯一值集合的场景,而WeakSet
通常用于需要跟踪对象但又不想阻止它们被垃圾回收的场景,如事件监听器管理等。如下例子:
<div id="wrap"> <div id="btn">确认</div> </div> <script> let wrap = document.getElementById("wrap") let btn = document.getElementById("btn") const disabledEls = new WeakSet() // disabledEls.add(btn) btn.addEventListener("click", () => { wrap.removeChild(btn) }) </script>
点击按钮后,清除按钮,这时我们不再需要按钮所产生的内存了,如果用
Set()
则会阻止内存回收,用WeakSet()
则不会,垃圾回收后则不会产生内存泄漏。
转载自:https://juejin.cn/post/7373609752238620735