日常学习2024.03.12-js中的weakSet
1.WeakSet是什么?
weakSet是什么呢?刚开始自己确实也没什么印象,终结学习一下吧。
WeakSet 是可被垃圾回收的值的集合,包括对象和非全局注册的符号(Symbol基本数据类型)。WeakSet 中的值只能出现一次。它在 WeakSet 的集合中是唯一的。
WeakSet 中的值一定是可被垃圾回收的值。大多数原始数据类型可以被任意地创建,并且没有生命周期,所以它们不能被存储。对象和非全局注册的符号可以被存储,因为它们是可被垃圾回收的值。
它和 Set 对象的主要区别有:
WeakSet 只能是对象和符号的集合,它不能像 Set 那样包含任何类型的任意值。
WeakSet 持弱引用:WeakSet 中对象的引用为弱引用。如果没有其他的对 WeakSet 中对象的引用存在,那么这些对象会被垃圾回收。
第一个区别好理解
const ws = new WeakSet();
ws.add(1) // 报错,Uncaught TypeError: Invalid value used in weak set
ws.add(Symbol()) // 不报错
第二个区别就有点懵了
2.WeakSet 使用弱引用,Set使用强引用
WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
这是因为垃圾回收机制根据对象的可达性(reachability)来判断回收,如果对象还能被访问到,垃圾回收机制就不会释放这块内存。结束使用该值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄漏。WeakSet 里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。因此,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。
由于上面这个特点,WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。
举个例子
let weakSet = new WeakSet();
// 创建两个对象
let obj1 = { name: 'Object 1' };
let obj2 = { name: 'Object 2' };
// 将对象添加到 WeakSet 中
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
console.log(weakSet.has(obj2)); // true
// 移除对 obj1 的引用
obj1 = null;
// 在这里,obj1 不再有任何强引用,可以被垃圾回收
console.log(weakSet.has(obj1)); // false
console.log(weakSet.has(obj2)); // true```
在上述示例中,我们创建了一个 WeakSet 实例 weakSet,并将两个对象 obj1 和 obj2 添加到其中。我们可以使用 has 方法检查对象是否存在于 WeakSet 中。
随后,我们将 obj1 的引用设置为 null,从而删除了对它的强引用。此时,obj1 不再有任何强引用,因此它可以被垃圾回收。
在最后的两个 console.log 语句中,我们可以看到 weakSet.has(obj1) 返回 false,表示 obj1 已经从 WeakSet 中被移除。而对于 obj2,它仍然存在于 WeakSet 中,因为它仍有一个强引用。
但是如果将WeakSet换成Set呢
let et = new Set();
// 创建两个对象
let obj1 = { name: 'Object 1' };
let obj2 = { name: 'Object 2' };
// 将对象添加到 WeakSet 中
et.add(obj1);
et.add(obj2);
console.log(et.has(obj1)); // true
console.log(et.has(obj2)); // true
// 移除对 obj1 的引用
obj1 = null;
console.log(et.has(obj1)); // false
console.log(et.has(obj2)); // true
结果还是一样的!那Set和WeakSet的第二个区别不就没有区别了吗。其实不是的
在上面的代码中,即使我们将 obj 设置为 null,Set 中的对象仍然不会被垃圾回收,因为 Set 本身保持了对它的强引用。如果我们试图检查 set.has(obj),由于 obj 是 null,这个检查没有意义。要正确检查 Set 是否包含对象,我们需要一个有效的引用到那个对象,这在我们的例子中是不存在的。
改一下代码
// 创建一个对象
let obj1 = { name: "Example1" };
// 创建一个 Set 并将对象添加进去
let set = new Set();
set.add(obj1);
// 此时,set 包含了对 obj1 的强引用
// 创建另一个引用,指向同一个对象
let obj2 = obj1;
// 移除 obj1 引用,但 obj2 仍然引用着对象
obj1 = null;
// 使用 obj2 检查对象是否仍然在 set 中
console.log(set.has(obj2)); // 输出 true,因为 obj2 仍然引用着对象,且对象在 set 中
// 现在,如果我们没有其他引用指向对象,并且尝试重新构造一个看似相同的对象,
// 它将不会被视为同一个对象,因为对象在 JavaScript 中是通过引用比较的
let obj3 = { name: "Example1" }; // 这是一个新的对象,与之前的 obj1 不是同一个引用
console.log(set.has(obj3)); // 输出 false,因为 obj3 是一个新的对象,尽管它的内容看起来与 set 中的对象相同
这个例子就能说明WeakSet 使用弱引用,这意味着如果没有其他强引用指向对象,对象可以被垃圾回收;而 Set 使用强引用,即使没有其他变量引用对象,Set 也会保持对象不被回收。
好好理解下,如果有问题欢迎指出