ES6之WeakMap

ES6之WeakMap

在Vue3的源码中,WeakMap被广泛应用于缓存对象。WeakMap提供了类似于Map的功能,但是键是弱引用的,也就是说当键对应的对象不再被其他对象所引用时,这个键值对会自动被GC(垃圾回收)回收

什么是 WeakMap

  • WeakMap 对象是一组键值对的集合,其中的键是 弱引用 的。其键必须是 对象,而值可以是任意的。

语法

  • Iterable 是一个数组(二元数组)或者其他可迭代的且其元素是键值对的对象。每个键值对会被加到新的 WeakMap 里。
new WeakMap([iterable])

方法

  • WeakMap 有四个方法:分别是 get、set、has、delete
const wm1 = new WeakMap(),
      wm2 = new WeakMap(),
      wm3 = new WeakMap();

const o1 = {},
      o2 = function() {},
      o3 = window;

wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // value 可以是任意值,包括一个对象或一个函数
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个 WeakMap 对象

wm1.get(o2); // "azerty"
wm2.get(o2); // undefined,wm2 中没有 o2 这个键
wm2.get(o3); // undefined,值就是 undefined

wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使值是 undefined)

wm3.set(o1, 37);
wm3.get(o1); // 37

wm1.has(o1);   // true
wm1.delete(o1);
wm1.has(o1);   // false

WeakMap的优点

相比较而言,在Map中保存了一些对象的引用,即使这些对象在其他地方都已经不再使用,但是由于它们仍被Map引用,所以它们不能被垃圾回收,这就可能导致内存泄漏

  • **减少内存泄漏:**因为WeakMap的键是弱引用的,当键不再被其他对象引用时,WeakMap 会自动释放对应的键值对,从而避免内存泄漏

  • **自动释放不再需要的对象:**由于WeakMap的弱引用特性,不再需要这些数据时,它们会被自动释放,不会造成内存泄漏

  • **提高性能:**不会阻止垃圾回收,避免占用过多的内存

WeakMap的缺点

  • 不支持遍历
  • 键必须是对象

WeakMap应用场景

解决内存泄漏问题

  • 内存泄漏是指程序中不再使用的对象仍然保留在内存中,导致内存占用过高,甚至可能导致程序崩溃。WeakMap 可以用来解决这个问题,因为它的键是弱引用的,当键不再被其他对象引用时,WeakMap 会自动释放对应的键值对,从而避免内存泄漏。
// 示例:使用 WeakMap 解决内存泄漏问题
class LeakyClass {
  constructor() {
    this.data = new Map();
  }

  // 添加数据
  setData(key, value) {
    this.data.set(key, value);
  }

  // 获取数据
  getData(key) {
    return this.data.get(key);
  }

  // 移除数据
  removeData(key) {
    this.data.delete(key);
  }
}

// 创建 LeakyClass 的实例
const leakyObject = new LeakyClass();

// 将对象添加到 WeakMap 中,并将其作为键
const weakMap = new WeakMap();
weakMap.set(leakyObject, 'some data');

// 断开对 leakyObject 的引用
leakyObject = null;

// 检查 WeakMap 中是否仍然存在对应键值对
console.log(weakMap.has(leakyObject)); 

  • 们创建了一个LeakyClass类,它有一个data属性,用于存储数据。我们将leakyObject添加到weakMap中,并将其作为键。然后,我们断开对leakyObject的引用,此时leakyObject成为垃圾回收的候选对象。最后,我们检查weakMap中是否仍然存在对应键值对。由于leakyObject已经不再被引用,它将被垃圾回收,因此weakMap.has(leakyObject)返回false。

避免循环引用

  • 循环引用是指两个或多个对象之间相互引用,形成一个循环,导致这些对象无法被垃圾回收。WeakSet 可以用来避免循环引用,因为它的成员是弱引用的,不会阻止垃圾回收。
// 示例:使用 WeakSet 避免循环引用
class Node {
  constructor(value) {
    this.value = value;
    this.children = new WeakSet();
  }

  addChild(node) {
    this.children.add(node);
  }

  removeChild(node) {
    this.children.delete(node);
  }
}

// 创建两个 Node 对象,并形成循环引用
const node1 = new Node(1);
const node2 = new Node(2);
node1.addChild(node2);
node2.addChild(node1);

// 将 node1 添加到 WeakSet 中
const weakSet = new WeakSet();
weakSet.add(node1);

// 断开对 node1 的引用
node1 = null;

// 等待垃圾回收
setTimeout(() => {
  // 检查 WeakSet 中是否仍然存在 node1
  console.log(weakSet.has(node1)); 
}, 1000);

  • 我们创建了两个Node对象,并通过addChild方法形成循环引用。然后,我们将node1添加到weakSet中。最后,我们断开对node1的引用,并等待垃圾回收。在垃圾回收之后,weakSet.has(node1)返回false,因为node1已经被回收

临时数据存储

  • WeakMap 和 WeakSet 可以用于存储临时数据,这些数据只在特定的时间段内有用。由于它们的弱引用特性,当不再需要这些数据时,它们会被自动释放,不会造成内存泄漏。
// 示例:使用 WeakMap 存储临时数据
const weakMap = new WeakMap();

// 创建一个临时对象,并将其添加到 WeakMap 中
const tempObject = {key: 'value'};
weakMap.set(tempObject, 'some data');

// 使用临时对象
console.log(weakMap.get(tempObject)); 

// 断开对临时对象的引用
tempObject = null;

// 等待垃圾回收
setTimeout(() => {
  // 检查 WeakMap 中是否仍然存在对应键值对
  console.log(weakMap.has(tempObject)); 
}, 1000);

我们使用WeakMap存储了一个临时对象和相关的数据。然后,我们断开对临时对象的引用,并等待垃圾回收。最后,WeakMap.has(tempObject)返回false,因为临时对象已经被回收

缓存

  • WeakMap 和 WeakSet 也可以用于缓存数据,尤其是在一些数据可能会变得很大的情况下。由于它们的弱引用特性,可以确保在不再需要这些数据时,它们会被自动释放,避免占用过多的内存
// 示例:使用 WeakMap 进行缓存
const weakMap = new WeakMap();

// 创建一个大型对象,并将其添加到 WeakMap 中
const largeObject = {key: 'value'};
weakMap.set(largeObject, 'cached data');

// 使用缓存的数据
console.log(weakMap.get(largeObject)); 

// 断开对大型对象的引用
largeObject = null;

// 等待垃圾回收
setTimeout(() => {
  // 检查 WeakMap 中是否仍然存在对应键值对
  console.log(weakMap.has(largeObject)); 
}, 1000);

  • 我们使用WeakMap缓存了一个大型对象。然后,我们断开对大型对象的引用,并等待垃圾回收。最后,WeakMap.has(largeObject)返回false,因为大型对象已经被回收

保存对象的私有数据

  • 正式由于WeakMap的不可遍历的缺点,可以利用这个特性来存储一些只有特定代码能够访问的数据

    let privateData = new WeakMap();
    function MyClass() {
      privateData.set(this, {
        secret: 'my secret data',
      });
    }
    MyClass.prototype.getSecret = function() {
      return privateData.get(this).secret;
    };
    let obj = new MyClass();
    console.log(obj.getSecret()); // 输出: 'my secret data'
    

好文推荐

  • https://www.jb51.net/javascript/306773luq.htm
  • 22
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值