JavaScript中的WeakRef与FinalizationRegistry
字数 559 2025-11-12 16:04:32

JavaScript中的WeakRef与FinalizationRegistry

描述
WeakRef(弱引用)和FinalizationRegistry(终结器注册表)是ES2021引入的两个高级特性,用于管理对象生命周期和垃圾回收。WeakRef允许您创建对对象的弱引用,不会阻止垃圾回收器回收目标对象;FinalizationRegistry允许您在对象被垃圾回收时执行清理回调。

基本概念

  1. 弱引用:不阻止垃圾回收的引用,当对象只有弱引用时会被回收
  2. 强引用:正常的引用,会阻止对象被垃圾回收
  3. 终结器:对象被回收时执行的回调函数

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;
}

最佳实践

  1. 谨慎使用:只在真正需要监控对象生命周期时使用
  2. 降级方案:提供不支持特性时的替代方案
  3. 避免依赖:不要依赖终结器的执行时机
  4. 资源管理:主要用于辅助资源管理,而非主要清理机制

WeakRef和FinalizationRegistry为JavaScript提供了更细粒度的内存管理能力,但需要谨慎使用,理解其特性和限制。

JavaScript中的WeakRef与FinalizationRegistry 描述 WeakRef(弱引用)和FinalizationRegistry(终结器注册表)是ES2021引入的两个高级特性,用于管理对象生命周期和垃圾回收。WeakRef允许您创建对对象的弱引用,不会阻止垃圾回收器回收目标对象;FinalizationRegistry允许您在对象被垃圾回收时执行清理回调。 基本概念 弱引用:不阻止垃圾回收的引用,当对象只有弱引用时会被回收 强引用:正常的引用,会阻止对象被垃圾回收 终结器:对象被回收时执行的回调函数 WeakRef详解 创建弱引用 弱引用的特性 FinalizationRegistry详解 创建终结器注册表 完整使用示例 实际应用场景 1. 缓存管理 2. 资源监控 注意事项 1. 不可预测的回收时机 2. 避免内存泄漏 3. 浏览器兼容性处理 最佳实践 谨慎使用 :只在真正需要监控对象生命周期时使用 降级方案 :提供不支持特性时的替代方案 避免依赖 :不要依赖终结器的执行时机 资源管理 :主要用于辅助资源管理,而非主要清理机制 WeakRef和FinalizationRegistry为JavaScript提供了更细粒度的内存管理能力,但需要谨慎使用,理解其特性和限制。