WeakSet

WeakSet是JavaScript中的一种数据结构,它存储的对象引用是弱引用,不会阻止垃圾回收。WeakSet的值只能是对象,不支持遍历,常用于临时存放对象或作为对象的辅助标记,例如储存DOM节点,防止内存泄漏。由于其特性,WeakSet在内存管理和防止循环引用方面有一定的优势。
摘要由CSDN通过智能技术生成

WeakSet

​ WeakSet 是 Set 的“兄弟”类型,其 API 也是 Set 的子集。WeakSet 中的“weak”(弱),描述的是 JavaScript 垃圾回收程序对待 “WeakSet” 中值的方式。

基本 API

​ 可以使用 new 关键字实例化一个空的 WeakSet:

const ws = new WeakSet();

​ WeakSet 中的值只能是 Object 或者继承自 Object 的类型,尝试使用非对象设置值会抛出 TypeError。

​ 如果想在初始化时填充WeakSet,则构造函数可以接收一个可迭代对象,其中需要包含有效的值。可迭代对象中的每个值都会按照迭代顺序插入到新实例中:

const val1 = {id: 1}, 
 val2 = {id: 2}, 
 val3 = {id: 3}; 
// 使用数组初始化弱集合
const ws1 = new WeakSet([val1, val2, val3]); 

alert(ws1.has(val1)); // true 
alert(ws1.has(val2)); // true 
alert(ws1.has(val3)); // true

​ WeakSet 结构有以下三个方法。

  • WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员,返回 WeakSet 结构本身。
  • WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员,清除成功返回true,如果在 WeakSet 中找不到该成员或该成员不是对象,返回false
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。

​ 初始化之后可以使用 add() 再添加新值,可以使用 has() 查询,还可以使用 **delete()**删除:

add() 方法返回弱集合实例,因此可以把多个操作连缀起来

const ws = new WeakSet(); 

const val1 = {id: 1}, 
 val2 = {id: 2}; 

alert(ws.has(val1)); // false 

ws.add(val1)
  .add(val2); 

alert(ws.has(val1)); // true 
alert(ws.has(val2)); // true 

ws.delete(val1); // 只删除这一个值

alert(ws.has(val1)); // false 
alert(ws.has(val2)); // true

弱值

​ WeakSet 中 “weak” 表示WeakSet的值是“弱弱地拿着”的。意思就是,这些值不属于正式的引用,不会阻止垃圾回收。

​ WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

​ 这是因为垃圾回收机制根据对象的可达性(reachability)来判断回收,如果对象还能被访问到,垃圾回收机制就不会释放这块内存。结束使用该值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄漏。WeakSet 里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。

​ 因此,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。

​ 来看下面的例子:

const ws = new WeakSet(); 
ws.add({}); 

​ add() 方法初始化了一个新对象,并将它用作一个值。因为没有指向这个对象的其他引用,所以当这行代码执行完成后,这个对象值就会被当作垃圾回收。然后,这个值就从WeakSet中消失了,使其成为一个空集合。

const ws = new WeakSet(); 

const container = { 
   val: {} 
}; 

ws.add(container.val); 

function removeReference() { 
   container.val = null; 
}

​ 这一次,container 对象维护着一个对WeakSet值的引用,因此这个对象值不会成为垃圾回收的目标。不过,如果调用了 removeReference(),就会摧毁值对象的最后一个引用,垃圾回收程序就可以把这个值清理掉。

不可迭代值

​ 因为 WeakSet 中的值任何时候都可能被销毁,所以没必要提供迭代其值的能力。当然,也用不着像 clear()这样一次性销毁所有值的方法。

​ WeakSet 没有size属性,没有办法遍历它的成员。

​ 下面代码试图获取sizeforEach属性,结果都不能成功。

ws.size // undefined
ws.forEach // undefined

ws.forEach(function(item){ console.log('WeakSet has ' + item)})
// TypeError: undefined is not a function

​ WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。

使用WeakSet

​ 相比于 WeakMap 实例,WeakSet 实例的用处没有那么大。

​ 一个用处,是储存 DOM 节点,给对象打标签,看下面的例子,这里使用了一个普通 Set:

const disabledElements = new Set(); 

const loginButton = document.querySelector('#login'); 

// 通过加入对应集合,给这个节点打上“禁用”标签
disabledElements.add(loginButton);

​ 这样,通过查询元素在不在 disabledElements 中,就可以知道它是不是被禁用了。不过,假如元素从 DOM 树中被删除了,它的引用却仍然保存在 Set 中,因此垃圾回收程序也不能回收它。

​ 为了让垃圾回收程序回收元素的内存,可以在这里使用 WeakSet:

const disabledElements = new WeakSet(); 

const loginButton = document.querySelector('#login'); 

// 通过加入对应集合,给这个节点打上“禁用”标签
disabledElements.add(loginButton);

​ 这样,只要 WeakSet 中任何元素从 DOM 树中被删除,垃圾回收程序就可以忽略其存在,而立即释放其内存(假设没有其他地方引用这个对象)。

​ 下面是使用 WeakSet 的另一个例子。

const foos = new WeakSet()
class Foo {
  constructor() {
    foos.add(this)
  }
  method () {
    if (!foos.has(this)) {
      throw new TypeError('Foo.prototype.method 只能在Foo的实例上调用!');
    }
  }
}

​ 上面代码保证了Foo的实例方法,只能在Foo的实例上调用。这里使用 WeakSet 的好处是,foos对实例的引用,不会被计入内存回收机制,所以删除实例的时候,不用考虑foos,也不会出现内存泄漏。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值