前言
在 JavaScript 中,Set
和 WeakSet
是不同的集合类型,它们在多个方面存在显著差异。本文将深入探讨它们的异同,从数据存储方式、引用特性、迭代能力到实际应用场景,帮助读者更全面地理解和运用这两种集合类型。
Set :
- 数据存储方式:
Set
是一种存储唯一值的集合,它不允许重复的元素。它可以包含任何数据类型,包括原始数据类型和对象引用。 - 引用:
Set
中的元素是强引用,这意味着当集合中的对象不再被引用时,集合仍然保留对该对象的引用。有时可能导致内存泄漏。 - 可迭代性:
Set
是可迭代的,可以使用for...of
循环或forEach
方法遍历集合中的元素。
WeakSet :
- 数据存储方式:
WeakSet
同样用于存储对象引用,但是它只允许存储对象引用,并且这些引用是弱引用。 - 引用:
WeakSet
中的对象引用是弱引用,这意味着如果在程序的其他地方没有对该对象的强引用,垃圾回收器可能会回收该对象,并从WeakSet
中移除。 - 可迭代性:
WeakSet
不是可迭代的,没有提供直接的方法来遍历其元素,因为元素的引用是弱引用,不稳定且无法确定何时会被垃圾回收。
它们的主要区别就是以下两点:
1. 类型限制:
-
Set:
Set
可以包含任何类型的值,包括原始数据类型和对象引用。jsconst mySet = new Set(); mySet.add(1); // 可以存储原始数据类型 mySet.add("Hello"); // 可以存储字符串 const obj = { key: "value" }; mySet.add(obj); // 可以存储对象引用
-
WeakSet:
WeakSet
的成员只能是对象和Symbol
值,不能包含其他类型的值。jsconst myWeakSet = new WeakSet(); myWeakSet.add({}); // 只能存储对象 const symbol = Symbol("symbol"); myWeakSet.add(symbol); // 可以存储Symbol值 myWeakSet.add(1); // 会报错,不能存储原始数据类型
2. 弱引用特性:
-
Set:
Set
中的元素是强引用,即使在程序的其他地方没有对该对象的引用,该对象仍然存在于Set中。 -
WeakSet:
WeakSet
中的对象是弱引用,如果在程序的其他地方没有对该对象的强引用,垃圾回收机制会自动回收该对象,同时也会从WeakSet
中移除。jsconst weakSet = new WeakSet(); let obj = { key: "value" }; weakSet.add(obj); console.log(weakSet.has(obj)); // true // 当没有其他引用指向obj时,垃圾回收机制会回收obj,并从WeakSet中移除 obj = null; console.log(weakSet.has(obj)); // false
方法和遍历:
- Set: 提供了
add
、delete
和has
等方法,以及可迭代的特性。 - WeakSet: 提供了
add
、delete
和has
等方法,但没有可迭代的特性,也没有size
属性。而且WeakSet
不能遍历,因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。
js
const mySet = new Set();
mySet.add(1);
mySet.delete(1);
console.log(mySet.has(1)); // false
console.log(mySet.size); // 获取Set的大小
const myWeakSet = new WeakSet();
const obj = {};
myWeakSet.add(obj);
myWeakSet.delete(obj);
console.log(myWeakSet.has(obj)); // false
console.log(myWeakSet.size); // undefined,WeakSet没有size属性
应用场景:
- Set: 适合需要存储任意类型值且不希望有重复值的情况。
- WeakSet: 适合临时存放一组对象,以及存放跟对象绑定的信息。由于
WeakSet
中的引用不会影响垃圾回收,因此适合用于防止内存泄漏,例如存储DOM节点,确保当节点被移除时,不会阻止其被垃圾回收。
下面来个WeakSet
实例:
js
const foos = new WeakSet()
class Foo {
constructor() {
foos.add(this)
}
method () {
if (!foos.has(this)) {
throw new TypeError('Foo.prototype.method 只能在Foo的实例上调用!');
}
}
}
const foos = new WeakSet()
:创建一个WeakSet
实例,用于存储Foo
类的实例引用。class Foo { ... }
:定义了一个Foo
类。- 在
Foo
类的构造函数中,foos.add(this)
:在foos
的WeakSet
中添加当前Foo
类的实例。这确保了Foo
的实例会被弱引用存储,不影响垃圾回收。 method()
:Foo
类的实例方法,用于执行某些操作。在方法的开头使用if (!foos.has(this))
检查this
是否在foos
中,如果不在,说明该方法不是在Foo
的实例上调用的,会抛出一个TypeError
,提示用户只能在Foo
的实例上调用该方法。
这段代码确保了Foo
的实例方法,只能在Foo
的实例上调用。这里使用 WeakSet 的好处是,foos
对实例的引用,不会被计入内存回收机制,所以删除实例的时候,不用考虑foos
,也不会出现内存泄漏。
总结
- 使用Set当你需要存储一组唯一的值,并且这些值在整个程序的生命周期内都是稳定的。
- 使用WeakSet当你需要存储对象引用,并且希望这些引用是弱引用,当对象在其他地方没有被强引用时可以被垃圾回收。
最后
总的来说,WeakSet
主要用于需要弱引用特性以避免内存泄漏的情况,而Set
适用于一般的集合需求。希望本文能够给你带来帮助!感谢阅读!
我的Gitee: CodeSpace (gitee.com)
技术小白记录学习过程,有错误或不解的地方还请评论区留言,如果这篇文章对你有所帮助请 "点赞 收藏+关注" ,感谢支持!!