JavaScript中的WeakRef与FinalizationRegistry
字数 549 2025-11-11 00:31:33
JavaScript中的WeakRef与FinalizationRegistry
描述
WeakRef和FinalizationRegistry是ES2021引入的两个高级API,用于管理对象的弱引用和垃圾回收后的清理操作。WeakRef允许您创建对对象的弱引用,不会阻止垃圾回收器回收对象;FinalizationRegistry允许您在对象被垃圾回收时执行清理回调。
核心概念
- 弱引用:一种不阻止垃圾回收的引用,当对象没有强引用时会被回收
- 垃圾回收:JavaScript引擎自动管理内存的机制
- 终结器:对象被回收后执行的回调函数
WeakRef详解
基本用法
// 创建普通引用(强引用)
let obj = { data: "重要数据" };
// 创建弱引用
let weakRef = new WeakRef(obj);
// 通过弱引用获取对象
let target = weakRef.deref();
console.log(target.data); // "重要数据"
// 清除强引用
obj = null;
// 此时对象可能已被垃圾回收,也可能还没被回收
setTimeout(() => {
let retrieved = weakRef.deref();
console.log(retrieved); // undefined(对象已被回收)或对象引用
}, 1000);
WeakRef使用场景
// 场景1:缓存大型对象,但不阻止垃圾回收
class Cache {
constructor() {
this.cache = new Map();
}
set(key, value) {
this.cache.set(key, new WeakRef(value));
}
get(key) {
const ref = this.cache.get(key);
if (ref) {
const value = ref.deref();
if (value === undefined) {
// 对象已被回收,清理缓存项
this.cache.delete(key);
}
return value;
}
return undefined;
}
}
FinalizationRegistry详解
基本用法
// 创建终结器注册表
const registry = new FinalizationRegistry((heldValue) => {
console.log(`对象已被回收,关联值: ${heldValue}`);
});
let obj = { data: "临时数据" };
let resource = "关联资源";
// 注册对象和关联值
registry.register(obj, resource);
// 清除引用
obj = null;
// 当obj被垃圾回收时,会触发回调并输出"对象已被回收,关联值: 关联资源"
完整示例:资源管理
class ResourceManager {
constructor() {
this.registry = new FinalizationRegistry((resourceId) => {
this.cleanupResource(resourceId);
});
this.resources = new Map();
}
// 分配资源
allocateResource(data) {
const resource = { id: Date.now(), data };
const weakRef = new WeakRef(resource);
this.resources.set(resource.id, weakRef);
this.registry.register(resource, resource.id);
return resource;
}
// 清理资源
cleanupResource(resourceId) {
console.log(`清理资源: ${resourceId}`);
this.resources.delete(resourceId);
}
// 获取资源
getResource(id) {
const ref = this.resources.get(id);
return ref ? ref.deref() : undefined;
}
}
// 使用示例
const manager = new ResourceManager();
let resource = manager.allocateResource("重要数据");
// 使用资源...
console.log(manager.getResource(resource.id)); // 正常访问
// 释放资源(只是清除引用,垃圾回收会自动触发清理)
resource = null;
注意事项和限制
- 执行时机不确定
// 垃圾回收时间不确定,不要依赖终结器的执行时机
registry.register(obj, "重要数据");
obj = null;
// 下面的代码执行时机不确定
// 可能在当前任务之后,也可能在很久之后
- 避免关键逻辑
// 错误用法:依赖终结器执行关键操作
class DatabaseConnection {
constructor() {
this.registry = new FinalizationRegistry(() => {
this.close(); // 不可靠!可能永远不会调用
});
}
close() {
// 关闭数据库连接
}
}
// 正确做法:显式管理资源
class DatabaseConnection {
close() {
// 显式关闭连接
}
}
- 浏览器兼容性处理
// 使用前检查支持情况
if (typeof WeakRef !== 'undefined' && typeof FinalizationRegistry !== 'undefined') {
// 使用WeakRef和FinalizationRegistry
const weakRef = new WeakRef(obj);
const registry = new FinalizationRegistry(cleanup);
} else {
// 降级方案:使用普通引用和手动清理
console.warn('WeakRef和FinalizationRegistry不被支持');
}
实际应用场景
- DOM元素缓存
class DOMCache {
constructor() {
this.cache = new Map();
this.registry = new FinalizationRegistry((elementId) => {
this.cache.delete(elementId);
});
}
// 缓存DOM元素引用
cacheElement(id, element) {
const weakRef = new WeakRef(element);
this.cache.set(id, weakRef);
this.registry.register(element, id);
return element;
}
// 获取缓存的元素
getElement(id) {
const ref = this.cache.get(id);
return ref ? ref.deref() : undefined;
}
}
- 大型对象监控
class LargeObjectMonitor {
constructor() {
this.registry = new FinalizationRegistry((objectInfo) => {
console.log(`大型对象 ${objectInfo.name} 已被回收,释放内存: ${objectInfo.size}MB`);
this.updateMemoryUsage(-objectInfo.size);
});
this.totalMemory = 0;
}
monitorObject(obj, name, sizeMB) {
this.registry.register(obj, { name, size: sizeMB });
this.updateMemoryUsage(sizeMB);
}
updateMemoryUsage(delta) {
this.totalMemory += delta;
console.log(`当前总内存使用: ${this.totalMemory}MB`);
}
}
最佳实践总结
- 谨慎使用,仅在真正需要弱引用语义时使用
- 不要依赖终结器执行关键业务逻辑
- 始终提供显式的资源清理方法作为备选方案
- 注意浏览器兼容性,提供降级方案
- 避免内存泄漏,确保及时清除不必要的引用
通过合理使用WeakRef和FinalizationRegistry,可以更精细地控制内存使用,但需要充分理解其特性和限制。