JavaScript中的WeakRef与FinalizationRegistry
字数 559 2025-11-12 16:04:32
JavaScript中的WeakRef与FinalizationRegistry
描述
WeakRef(弱引用)和FinalizationRegistry(终结器注册表)是ES2021引入的两个高级特性,用于管理对象生命周期和垃圾回收。WeakRef允许您创建对对象的弱引用,不会阻止垃圾回收器回收目标对象;FinalizationRegistry允许您在对象被垃圾回收时执行清理回调。
基本概念
- 弱引用:不阻止垃圾回收的引用,当对象只有弱引用时会被回收
- 强引用:正常的引用,会阻止对象被垃圾回收
- 终结器:对象被回收时执行的回调函数
WeakRef详解
创建弱引用
let obj = { data: 'important data' };
// 创建对obj的弱引用
let weakRef = new WeakRef(obj);
// 通过deref()方法获取原对象
let original = weakRef.deref();
console.log(original.data); // 'important data'
弱引用的特性
let obj = { value: 42 };
let weakRef = new WeakRef(obj);
// 移除强引用
obj = null;
// 此时weakRef.deref()可能返回undefined(取决于垃圾回收时机)
// 因为obj只有弱引用,可能已被回收
setTimeout(() => {
let retrieved = weakRef.deref();
console.log(retrieved); // 可能是undefined
}, 1000);
FinalizationRegistry详解
创建终结器注册表
// 创建注册表,指定回调函数
const registry = new FinalizationRegistry((heldValue) => {
console.log(`对象已被回收,关联值: ${heldValue}`);
});
let obj = { data: 'test' };
// 注册对象和关联值
registry.register(obj, 'important resource');
完整使用示例
const registry = new FinalizationRegistry((heldValue) => {
console.log(`清理资源: ${heldValue}`);
});
function createResource() {
const resource = { data: new Array(1000).fill('data') };
const resourceId = 'resource_' + Math.random();
// 注册终结器
registry.register(resource, resourceId);
return resource;
}
let myResource = createResource();
// 使用资源...
console.log(myResource.data.length);
// 不再需要资源时
myResource = null;
// 垃圾回收后,会触发终结器回调
实际应用场景
1. 缓存管理
class WeakCache {
constructor() {
this.cache = new Map();
this.registry = new FinalizationRegistry((key) => {
console.log(`清理缓存键: ${key}`);
this.cache.delete(key);
});
}
set(key, value) {
const weakRef = new WeakRef(value);
this.cache.set(key, weakRef);
// 注册终结器,当value被回收时清理缓存
this.registry.register(value, key);
return value;
}
get(key) {
const weakRef = this.cache.get(key);
if (weakRef) {
const value = weakRef.deref();
if (value) {
return value;
} else {
// 对象已被回收,清理缓存项
this.cache.delete(key);
}
}
return null;
}
}
// 使用示例
const cache = new WeakCache();
let largeObject = { data: new Array(10000) };
cache.set('large-data', largeObject);
// 当largeObject=null且被回收后,缓存自动清理
2. 资源监控
class ResourceMonitor {
constructor() {
this.registry = new FinalizationRegistry((resourceInfo) => {
console.warn(`资源未正确释放:`, resourceInfo);
this.cleanupResource(resourceInfo);
});
this.resources = new Set();
}
trackResource(resource, cleanupCallback) {
const resourceInfo = {
type: resource.constructor.name,
createdAt: new Date(),
cleanup: cleanupCallback
};
this.resources.add(resourceInfo);
this.registry.register(resource, resourceInfo);
return resource;
}
cleanupResource(resourceInfo) {
if (resourceInfo.cleanup) {
resourceInfo.cleanup();
}
this.resources.delete(resourceInfo);
}
manualCleanup(resource) {
// 手动清理资源
this.resources.forEach(info => {
if (info.resource === resource) {
this.cleanupResource(info);
}
});
}
}
注意事项
1. 不可预测的回收时机
// 垃圾回收时机不确定,不要依赖终结器执行时机
const registry = new FinalizationRegistry(() => {
// 这可能在程序退出时才执行,也可能永远不执行
console.log('对象被回收');
});
// 不要用于关键资源清理
// 应该使用显式的清理方法
2. 避免内存泄漏
// 错误用法:终结器回调中引用被回收对象
class LeakyExample {
constructor() {
this.registry = new FinalizationRegistry((target) => {
// 错误:在回调中引用target会阻止其被回收
console.log(target.someProperty); // 这会导致内存泄漏
});
}
}
// 正确用法:只使用注册时提供的关联值
class CorrectExample {
constructor() {
this.registry = new FinalizationRegistry((heldValue) => {
console.log(heldValue); // 只使用关联值
});
}
}
3. 浏览器兼容性处理
// 检查特性支持
function setupFinalization() {
if (typeof FinalizationRegistry === 'undefined' ||
typeof WeakRef === 'undefined') {
console.warn('WeakRef或FinalizationRegistry不被支持');
return {
register: () => {}, // 空函数作为降级方案
unregister: () => {}
};
}
const registry = new FinalizationRegistry((heldValue) => {
// 正常的终结器逻辑
});
return registry;
}
最佳实践
- 谨慎使用:只在真正需要监控对象生命周期时使用
- 降级方案:提供不支持特性时的替代方案
- 避免依赖:不要依赖终结器的执行时机
- 资源管理:主要用于辅助资源管理,而非主要清理机制
WeakRef和FinalizationRegistry为JavaScript提供了更细粒度的内存管理能力,但需要谨慎使用,理解其特性和限制。