JavaScript中的WeakRef与FinalizationRegistry
字数 549 2025-11-11 00:31:33

JavaScript中的WeakRef与FinalizationRegistry

描述
WeakRef和FinalizationRegistry是ES2021引入的两个高级API,用于管理对象的弱引用和垃圾回收后的清理操作。WeakRef允许您创建对对象的弱引用,不会阻止垃圾回收器回收对象;FinalizationRegistry允许您在对象被垃圾回收时执行清理回调。

核心概念

  1. 弱引用:一种不阻止垃圾回收的引用,当对象没有强引用时会被回收
  2. 垃圾回收:JavaScript引擎自动管理内存的机制
  3. 终结器:对象被回收后执行的回调函数

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;

注意事项和限制

  1. 执行时机不确定
// 垃圾回收时间不确定,不要依赖终结器的执行时机
registry.register(obj, "重要数据");
obj = null;

// 下面的代码执行时机不确定
// 可能在当前任务之后,也可能在很久之后
  1. 避免关键逻辑
// 错误用法:依赖终结器执行关键操作
class DatabaseConnection {
    constructor() {
        this.registry = new FinalizationRegistry(() => {
            this.close(); // 不可靠!可能永远不会调用
        });
    }
    
    close() {
        // 关闭数据库连接
    }
}

// 正确做法:显式管理资源
class DatabaseConnection {
    close() {
        // 显式关闭连接
    }
}
  1. 浏览器兼容性处理
// 使用前检查支持情况
if (typeof WeakRef !== 'undefined' && typeof FinalizationRegistry !== 'undefined') {
    // 使用WeakRef和FinalizationRegistry
    const weakRef = new WeakRef(obj);
    const registry = new FinalizationRegistry(cleanup);
} else {
    // 降级方案:使用普通引用和手动清理
    console.warn('WeakRef和FinalizationRegistry不被支持');
}

实际应用场景

  1. 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;
    }
}
  1. 大型对象监控
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`);
    }
}

最佳实践总结

  1. 谨慎使用,仅在真正需要弱引用语义时使用
  2. 不要依赖终结器执行关键业务逻辑
  3. 始终提供显式的资源清理方法作为备选方案
  4. 注意浏览器兼容性,提供降级方案
  5. 避免内存泄漏,确保及时清除不必要的引用

通过合理使用WeakRef和FinalizationRegistry,可以更精细地控制内存使用,但需要充分理解其特性和限制。

JavaScript中的WeakRef与FinalizationRegistry 描述 WeakRef和FinalizationRegistry是ES2021引入的两个高级API,用于管理对象的弱引用和垃圾回收后的清理操作。WeakRef允许您创建对对象的弱引用,不会阻止垃圾回收器回收对象;FinalizationRegistry允许您在对象被垃圾回收时执行清理回调。 核心概念 弱引用:一种不阻止垃圾回收的引用,当对象没有强引用时会被回收 垃圾回收:JavaScript引擎自动管理内存的机制 终结器:对象被回收后执行的回调函数 WeakRef详解 基本用法 WeakRef使用场景 FinalizationRegistry详解 基本用法 完整示例:资源管理 注意事项和限制 执行时机不确定 避免关键逻辑 浏览器兼容性处理 实际应用场景 DOM元素缓存 大型对象监控 最佳实践总结 谨慎使用,仅在真正需要弱引用语义时使用 不要依赖终结器执行关键业务逻辑 始终提供显式的资源清理方法作为备选方案 注意浏览器兼容性,提供降级方案 避免内存泄漏,确保及时清除不必要的引用 通过合理使用WeakRef和FinalizationRegistry,可以更精细地控制内存使用,但需要充分理解其特性和限制。