FinalizationRegistry
[TOC]
索引
构造方法:
- new FinalizationRegistry():
(cleanupCallback)
,ES2021,允许开发者注册一个回调函数,当对象被垃圾回收时自动执行清理操作。
方法:
- registry.register():
(target, heldValue, unregisterToken?)
,ES2021,用于注册需要垃圾回收监视的对象。 - registry.unregister():
(unregisterToken)
,ES2021,用于取消之前通过register()
方法设置的垃圾回收监听。
FinalizationRegistry
构造方法
new FinalizationRegistry()@
new FinalizationRegistry():(cleanupCallback)
,ES2021,允许开发者注册一个回调函数,当对象被垃圾回收时自动执行清理操作。
cleanupCallback:
(heldValue)=>void
,当注册对象被垃圾回收时触发的清理回调函数。- heldValue:
any
,注册对象时提供的标识值。
- heldValue:
返回:
registry:
FinalizationRegistry
,返回一个实例对象。
核心特性:
完整类型定义:
tsdeclare class FinalizationRegistry { constructor(cleanupCallback: (heldValue: any) => void); register( target: object, heldValue: any, unregisterToken?: object ): void; unregister(unregisterToken: object): boolean; }
不可预测性:
- 回调执行时间不确定(可能延迟数分钟)
- 程序崩溃/退出时回调不会触发
弱引用原则:强引用会导致对象无法回收
优先使用显式资源管理:
js// 使用 finally 块确保清理 try { const resource = acquireResource(); } finally { releaseResource(); // 确定性的清理 }
示例:
文件句柄管理:
js// 文件句柄管理 const fileRegistry = new FinalizationRegistry(fileId => { console.log(`自动关闭文件: ${fileId}`); fs.closeSync(fileId); }); function openFile(path) { const handle = fs.openSync(path, 'r'); const token = { path }; // 取消令牌 fileRegistry.register(handle, handle.fd, token); return { handle, token }; } // 使用文件 const { handle, token } = openFile('data.txt'); readFile(handle); // 显式关闭(优先选择) fileRegistry.unregister(token); fs.closeSync(handle.fd); // 若忘记关闭,垃圾回收后会触发自动关闭
方法
register()@
registry.register():(target, heldValue, unregisterToken?)
,ES2021,用于注册需要垃圾回收监视的对象。
target:
object
,要监视垃圾回收的对象。要求:必须是对象类型(非原始值)
对
target
的引用是弱引用,不会阻止垃圾回收。
heldValue:
any
,回调触发时传递给清理函数的值。最佳实践:推荐使用字符串/数字/简单对象。
避免直接引用
target
本身(防止内存泄漏)。
unregisterToken?:
object|undefined
,用于后续取消注册的令牌。要求:必须是对象类型(如果提供)。
建议使用专用令牌对象。
核心特性:
覆盖注册:
同一对象多次注册会覆盖之前的注册:
jsregistry.register(obj, "first"); registry.register(obj, "second"); // 覆盖第一次注册 // 回收时只会触发 "second" 的回调
多次注册:
同一对象可在不同注册表独立注册:需要同时注册多个时使用不同注册表。
jsconst reg1 = new FinalizationRegistry(v => console.log(`Reg1: ${v}`)); const reg2 = new FinalizationRegistry(v => console.log(`Reg2: ${v}`)); reg1.register(obj, "id1"); reg2.register(obj, "id2"); // 回收时会触发两个回调
令牌使用:
同一令牌可注册多个对象:
jsconst token = {}; registry.register(obj1, "res1", token); registry.register(obj2, "res2", token); // 取消注册会移除所有关联对象 registry.unregister(token);
不同令牌独立管理:
jsconst tokenA = {}; const tokenB = {}; registry.register(obj, "A", tokenA); registry.register(obj, "B", tokenB); // 覆盖第一次注册 // 取消 tokenA 无效(已被覆盖) registry.unregister(tokenA); // 返回 false // 取消 tokenB 有效 registry.unregister(tokenB); // 返回 true
垃圾回收条件:
对象被回收的必要条件:
- 必须移除所有强引用
- 可能需要手动触发GC(仅开发环境)
jslet obj = {}; registry.register(obj, "value"); // 必须移除所有强引用 obj = null; // 可能需要手动触发GC(仅开发环境) globalThis.gc?.();
unregister()
registry.unregister():(unregisterToken)
,ES2021,用于取消之前通过 register()
方法设置的垃圾回收监听。
unregisterToken:
object
,用于标识要取消的注册项。要求:必须是对象类型(非原始值)
必须与
register()
时使用的令牌严格相等(===
)
返回:
isCanceled:
boolean
,返回是否成功取消了注册。
核心特性:
取消注册关系:
- 移除对象与清理回调的关联
- 目标对象被回收时不再触发回调
jsconst token = {}; const obj = {}; registry.register(obj, "important", token); registry.unregister(token); // 取消注册 obj = null; // 即使被回收,也不会触发回调
令牌的多重注册管理:
- 一个令牌可关联多个对象
- 取消操作会移除所有关联项
jsconst token = {}; const obj1 = {}; const obj2 = {}; registry.register(obj1, "res1", token); registry.register(obj2, "res2", token); // 取消所有使用此令牌的注册 registry.unregister(token); // 返回 true // 两个对象都不再被监视
令牌引用要求:
必须保留对令牌的引用才能取消
js// 错误:无法保留令牌引用 registry.register(obj, "value", {}); // 无法取消,因为没有令牌引用 registry.unregister(/* 无可用令牌 */);
示例:
使用规则:
js// 创建令牌 const token = { id: "unique-token" }; // 注册对象 registry.register(targetObj, "value", token); // 正确取消 registry.unregister(token); // ✅ // 错误示例 registry.unregister({ id: "unique-token" }); // ❌ 不同对象 registry.unregister("unique-token"); // ❌ 原始值(TypeError)
应用场景:
网络连接资源管理:
jsclass DatabaseConnection { constructor() { this.registry = new FinalizationRegistry(connId => { console.warn(`未关闭的连接: ${connId}`); this.forceClose(connId); }); this.connection = createConnection(); this.token = { connId: this.connection.id }; this.registry.register( this.connection, this.connection.id, this.token ); } close() { if (this.connection) { // 正常关闭连接 this.connection.close(); // 取消注册防止重复清理 const success = this.registry.unregister(this.token); console.log(success ? "安全取消" : "已自动清理"); this.connection = null; } } } // 使用示例 const db = new DatabaseConnection(); // 正常关闭 db.close(); // 输出 "安全取消" // 忘记关闭 // 垃圾回收后输出 "未关闭的连接: [id]"