Javascript 是一门垃圾回收语言,也就是说,除非主动删除或者无法在背后访问的对象,不然,内存空间将不会自动释放。因此,在代码运行过程中,我们必须十分小心地管理数据的生命周期以避免出现内存泄漏的情况。ES2021 新功能中引入了 WeakRefs
——Weakref 对象,它可以帮助我们更好地解决内存泄漏问题。
WeakRefs
是什么
WeakRefs
是一种新的 Javascript 对象类型,用来存储存留引用但不会增加内存计数的对象引用。这个对象“弱引用”的概念意味着,当一个 WeakRefs
对象所指向的对象没有其他的强引用时,这个对象就会被垃圾回收器自动回收掉。
WeakRefs
的好处
在某些情况下,我们需要在代码中保留某些数据的引用,但是不想在避免内存泄漏的前提下增加内存计数。之前,我们可能会通过手动实现一些算法来达到这个目的,而使用 WeakRefs
对象则可以更好地解决这个问题,一些好处包括:
- 更简单地管理内存泄漏:
WeakRefs
自动指示当一个存留引用的对象不再需要时,垃圾回收器将它自动删除,不再需要手动实现一些算法。 - 更直接的语言支持:ES2021 把
WeakRefs
纳入了 Javascript 标准之中,意味着在使用 TypeScript 等编译器时,我们可以更容易地使用这个功能。
为了更好地理解这个好处,让我们看一下下面的一个例子:
// javascriptcn.com 代码示例 class BigObject { constructor() { this.data = new Array(1000000).fill('data'); this.callbacks = new Map(); } on(event, fn) { this.callbacks.set(event, fn); } trigger(event) { this.callbacks.get(event)(); } } let mySingletonInstance = null; function getBigObjectInstance() { if (mySingletonInstance === null) { mySingletonInstance = new BigObject(); } return mySingletonInstance; } const obj = getBigObjectInstance(); // 这里有一些回调函数 obj.on('dataChanged', () => { console.log('data has changed'); }); obj.on('dataSaved', () => { console.log('data has been saved'); }); // 像这样做内存清理 // obj.callbacks.clear(); // obj = null; // 一段时间之后 // obj.trigger('dataChanged'); // obj.trigger('dataSaved');
在上面的例子中,我们模拟了一个典型的单例模式,其中有一个 BigObject
类,我们只会实例化它一次。然而,我们却定义了两个回调函数,这些函数将注册在 BigObject
的实例对象上。如果我们不在回调执行后将它们从 callbacks
Map 中删除,就会导致在对象失去引用变得不可访问时也仍然占用内存。很明显,我们可以通过调用 obj.callbacks.clear()
和 obj = null
来清理内存,但是如果我们忘记了这样做,那么就可能会出现内存泄漏的问题。而如果我们加入 WeakRefs
的支持,这些问题便不复存在了。考虑下面的示例代码:
// javascriptcn.com 代码示例 class BigObject { constructor() { this.data = new Array(1000000).fill('data'); this.callbacks = new Map(); } on(event, fn) { this.callbacks.set(event, new WeakRef(fn)); } trigger(event) { this.callbacks.get(event)()?.deref()(); } } let mySingletonInstance = null; function getBigObjectInstance() { if (mySingletonInstance === null) { mySingletonInstance = new BigObject(); } return mySingletonInstance; } const obj = getBigObjectInstance(); // 这里有一些回调函数 obj.on('dataChanged', () => { console.log('data has changed'); }); obj.on('dataSaved', () => { console.log('data has been saved'); }); // 像这样做内存清理 // obj.callbacks.clear(); // obj = null; // 一段时间之后 // obj.trigger('dataChanged'); // obj.trigger('dataSaved');
在上面的代码中,我们用 WeakRef
替换了强引用,并在 on
方法中添加了相应的修改,使得每一个回调都变成了一种弱引用。这样,即使在 callbacks
Map 中还保留着该回调对象,但是消失了它的任何其他强引用,这个对象依然可以被垃圾回收器自动删除掉。
WeakRefs
的局限
虽然 WeakRefs
对象很好地解决了一些内存泄漏的问题,但是它也有一些局限。
- 对象不可直接访问:
WeakRefs
对象不能直接访问其所指向的对象,因为其指向的对象可能已经被回收了。 - 弱引用不支持用作键或值:由于
WeakRefs
对象会随时被垃圾回收器删除,因此不能再其所在的Map
或Set
中用作键或值。
总结
ES2021 新功能中的 WeakRefs
——Weakref 对象可以很好地解决内存泄漏的问题,使用它的好处包括更简单地管理内存泄漏和更直接的语言支持。在使用这个功能的时候,我们需要注意其一些局限,包括其不能直接访问其所指向的对象以及不能再其所在的 Map
或 Set
中用作键或值。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6534beed7d4982a6eb9e3377