Subverting control with weak references

J Longster 探讨了 JavaScript 中弱引用(Weak References)的使用及其带来的抽象能力,尤其是通过 WeakMapWeakRef 两个 API 实现的弱引用功能。

文章首先介绍了弱引用的基本概念:与普通引用不同,弱引用不会阻止垃圾回收器对对象的回收。例如,通过 WeakRefderef 方法获取对象时,如果对象已被回收,则返回 undefined。这种特性使得弱引用在缓存等场景中非常有用,因为它可以避免内存泄漏。

作者重点讨论了 WeakMap 的使用场景。与普通 Map 不同,WeakMap 的键是弱引用,而值是强引用。这意味着只要键对象存在,值对象也会被保留;而当键对象被垃圾回收后,对应的值也会从 WeakMap 中移除。这种特性使得 WeakMap 成为实现缓存功能的理想选择,因为它可以自动清理不再使用的对象,而无需手动管理内存。

文章通过一个示例展示了如何利用 WeakMap 实现缓存功能。假设有一个 Transaction 类,需要通过远程服务器获取额外信息来生成文本描述。为了避免重复请求,可以使用 WeakMap 缓存结果:

const CACHE = new WeakMap();

async function describe(transaction) {
  let cached = CACHE.get(transaction);
  if (cached) {
    return cached;
  }

  // 执行获取信息和生成描述的逻辑
  CACHE.set(transaction, description);
  return description;
}

这种方式的优点是无需担心内存泄漏,因为当 transaction 对象被垃圾回收后,其对应的缓存也会自动失效。

此外,作者还探讨了弱引用在扩展现有类功能时的应用。例如,假设无法修改 Transaction 类,但需要为其添加货币转换功能,可以使用 WeakMap 来存储与每个 Transaction 实例关联的货币转换器实例。这种方式类似于“扩展”类的功能,而无需直接修改原类:

const CONVERTERS = new WeakMap();

function getConverter(transaction) {
  const converter = CONVERTERS.get(transaction) || new CurrencyConversion(transaction);
  CONVERTERS.set(transaction, converter);
  return converter;
}

通过这种方式,可以在不改变原有类结构的情况下,为其实例添加额外的功能和状态,同时避免了内存泄漏的问题。

文章最后指出,这种基于弱引用的编程模式虽然是一种边缘情况,但在实际开发中,尤其是在大型项目中,这种能力可以极大地提升代码的灵活性和可维护性。作者鼓励开发者在设计代码结构和抽象时,充分利用这些工具来优化代码的可扩展性和可维护性。