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