随着 JavaScript 应用程序的复杂性不断增加,内存泄漏越来越成为一个常见的问题。虽然 JavaScript 具有垃圾回收机制,但是在某些情况下,可能会出现一些变量或对象没有被垃圾回收导致内存泄漏的情况。ES2021 引入了 WeakRefs,这是一种新的 JavaScript 引用类型,它提供了解决这些问题的方法。
什么是 WeakRefs
在 Javascript 中,我们可以使用引用类型对变量或对象进行引用。例如:
const obj = { foo: 'bar' };
const arr = [1, 2, 3];
const str = 'hello world';当我们将这些值赋给变量时,变量实际上只是对它们的引用。当没有任何变量引用这些值时,它们将成为垃圾回收的候选对象。
然而,有些情况下,我们需要在变量不再引用某个对象时自动执行某些操作。这时候,如果我们强制使用引用类型,就无法在变量引用计数为 0 的时候自动执行操作。而 WeakRefs 却可以解决这个问题。
WeakRefs 是一种新的 JavaScript 引用类型,它可以在被引用对象被垃圾回收时自动执行回调函数。WeakRefs 可以用来解决一些内存泄漏的问题。
WeakRefs 的原理
WeakRefs 本质上是一种弱引用。在 Javascript 中,通过强引用可以保证一个对象不被垃圾回收,只有当所有引用对象都被删除时,它才会被垃圾回收。而 WeakRefs 的作用是,当强引用被删除时,WeakRefs 会自动 abort 即中断操作,这时候回调函数可以被触发。
为了使用 WeakRefs,我们需要创建一个 WeakRef 对象。当我们创建一个 WeakRef 对象时,它会被关联到一个对象。当这个对象被垃圾回收时,WeakRef 对象会自动被释放,它所关联的回调函数也会自动执行。
const obj = new MyClass(); const weakRef = new WeakRef(obj); // 当 obj 被垃圾回收时,weakRef 回调函数会自动执行。
回调函数的执行方式可以使用某些方法定义。例如,我们可以使用 onnotify 方法:
const obj = new MyClass();
const weakRef = new WeakRef(obj);
weakRef.onnotify = () => {
console.log('对象已被垃圾回收');
}WeakRefs 的应用
WeakRefs 可以应用于一些内存泄漏的场景,例如在采用自定义事件使用的场景。
// www.javascriptcn.com code example
class EventEmitter {
constructor() {
this.listeners = new Map();
}
on(type, listener) {
if (!this.listeners.has(type)) {
this.listeners.set(type, []);
}
const listeners = this.listeners.get(type);
listeners.push(listener);
}
emit(type, ...args) {
const listeners = this.listeners.get(type);
if (!listeners) {
return;
}
for (const listener of listeners) {
listener(...args);
}
}
}
const eventEmitter = new EventEmitter();
class MyClass {
constructor() {
eventEmitter.on('foo', this.handleFoo);
}
handleFoo() {
console.log('foo');
}
}
const obj = new MyClass();在上面的代码中,MyClass 实例化时会订阅 EventEmitter 的 foo 事件。如果 MyClass 实例被垃圾回收,但没有取消订阅该事件,则 EventEmitter 中的监听函数池仍然持有 MyClass 实例的引用。当 MyClass 实例和 WeakRefs 结合使用时,我们可以在 MyClass 实例被垃圾回收时自动取消订阅该事件:
// www.javascriptcn.com code example
class MyClass {
constructor() {
eventEmitter.on('foo', this.handleFoo);
this.weakRef = new WeakRef(this);
}
handleFoo() {
console.log('foo');
}
}
const obj = new MyClass();这样一来,当 MyClass 实例被垃圾回收时,WeakRef 会自动将 handleFoo 函数解除订阅。
除此之外,WeakRefs 还可以用来优化内存占用。例如,在某些场景下,我们需要缓存某些对象,并在一段时间后自动清除这些缓存的对象。使用 Map 对象可以轻松实现这个功能,但是这些缓存的对象永远不会被垃圾回收,也就占用了大量的内存。如果我们使用 WeakRefs 来存储这些缓存对象,则可以让这些对象在不再使用时自动被垃圾回收,从而优化内存占用。
// www.javascriptcn.com code example
const cache = new Map();
function getValue(key) {
if (cache.has(key)) {
const weakRef = cache.get(key);
const value = weakRef.deref();
if (value) {
return value;
} else {
cache.delete(key);
}
}
const value = getValueFromDB(key);
const weakRef = new WeakRef(value);
cache.set(key, weakRef);
return value;
}在上面的代码中,我们使用了 Map 存储缓存对象,同时使用 WeakRef 关联对象。在 getValue 函数中,当缓存对象过期或被垃圾回收时,WeakRef 自动删除与之相关的缓存对象。
总结
ES2021 中引入了 WeakRefs,通过它我们可以解决一些内存泄漏的问题,可以优化内存占用,使我们的前端应用程序更加高效和稳定。WeakRefs 目前在大多数现代浏览器中得到了支持,是我们在前端开发中不可忽视的一种新的 Javascript 引用类型。
Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/64c669ae10032fedd38d0dcf