Subverting control with weak references
J Longster 探讨了 JavaScript 中弱引用(Weak References)的使用及其带来的抽象能力,尤其是通过 WeakMap
和 WeakRef
两个 API 实现的弱引用功能。
文章首先介绍了弱引用的基本概念:与普通引用不同,弱引用不会阻止垃圾回收器对对象的回收。例如,通过 WeakRef
的 deref
方法获取对象时,如果对象已被回收,则返回 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;
}
通过这种方式,可以在不改变原有类结构的情况下,为其实例添加额外的功能和状态,同时避免了内存泄漏的问题。
文章最后指出,这种基于弱引用的编程模式虽然是一种边缘情况,但在实际开发中,尤其是在大型项目中,这种能力可以极大地提升代码的灵活性和可维护性。作者鼓励开发者在设计代码结构和抽象时,充分利用这些工具来优化代码的可扩展性和可维护性。