导读:本期聚焦于小伙伴创作的《JavaScript中的弱引用WeakRef和终结器FinalizationRegistry如何管理内存》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《JavaScript中的弱引用WeakRef和终结器FinalizationRegistry如何管理内存》有用,将其分享出去将是对创作者最好的鼓励。

JavaScript的垃圾回收机制基于可达性分析,当一个对象无法被任何活跃引用访问到时,就会被标记为可回收。但默认的强引用会让对象只要被引用就无法回收,在某些场景下会导致不必要的内存占用。弱引用WeakRef和终结器FinalizationRegistry就是为了解决这个问题而出现的,它们让开发者可以更灵活地控制对象的生命周期。

JavaScript中的弱引用WeakRef和终结器FinalizationRegistry如何管理内存

弱引用WeakRef的基本原理

WeakRef可以创建一个对对象的弱引用,这个引用不会阻止对象被垃圾回收。和普通的强引用不同,即使存在WeakRef指向某个对象,只要该对象没有其他强引用,垃圾回收器就可以回收它。

创建WeakRef的方式很简单,只需要把目标对象作为参数传入WeakRef构造函数即可:

// 创建一个普通对象
const targetObj = { name: "test" };
// 创建该对象的弱引用
const weakRef = new WeakRef(targetObj);

// 通过deref方法获取弱引用指向的对象
// 如果对象还未被回收,返回对象本身;如果已经被回收,返回undefined
const derefResult = weakRef.deref();
console.log(derefResult); // 输出 { name: "test" }

需要注意的是,deref方法的返回结果是不确定的,因为垃圾回收的触发时机是不确定的。你无法主动触发垃圾回收,也无法知道对象是否已经被回收,只能通过调用deref来尝试获取对象。

终结器FinalizationRegistry的作用

FinalizationRegistry可以在注册的对象被垃圾回收之后,执行指定的回调函数。这个特性可以用来做一些清理工作,比如释放和对象关联的资源、记录对象被回收的日志等。

使用FinalizationRegistry需要先创建一个注册表实例,然后调用register方法注册需要监听的对象,还可以传入一个可选的持有值(heldValue)和可选的取消注册令牌(unregisterToken):

// 创建终结器注册表,传入对象被回收后执行的回调
const registry = new FinalizationRegistry((heldValue) => {
  console.log(`对象被回收了,关联的持有值是:${heldValue}`);
});

// 创建目标对象
const obj = { id: 1 };
// 注册对象,第二个参数是持有值,会在回调中传入
registry.register(obj, "obj的持有值");

// 解除对obj的强引用,让它可以成为垃圾回收的目标
// 注意:这里只是示例,实际中需要确保所有强引用都被移除
// obj = null; 

如果需要取消注册,可以调用unregister方法,传入之前注册时使用的取消注册令牌。如果没有传入取消注册令牌,就无法取消注册。

两者的配合使用场景

WeakRef和FinalizationRegistry经常配合使用,实现一些需要跟踪对象生命周期的功能。最常见的场景是实现一个不会阻止对象回收的缓存:

class WeakCache {
  constructor() {
    // 缓存映射,key是缓存键,value是WeakRef
    this.cache = new Map();
    // 终结器注册表,对象被回收时清理缓存
    this.registry = new FinalizationRegistry((key) => {
      this.cache.delete(key);
      console.log(`缓存键${key}对应的对象已被回收,缓存已清理`);
    });
  }

  set(key, value) {
    // 创建弱引用存入缓存
    const weakRef = new WeakRef(value);
    this.cache.set(key, weakRef);
    // 注册对象到终结器,对象回收时清理对应缓存
    this.registry.register(value, key);
  }

  get(key) {
    const weakRef = this.cache.get(key);
    if (!weakRef) return undefined;
    // 尝试获取对象
    const value = weakRef.deref();
    if (!value) {
      // 对象已被回收,清理缓存
      this.cache.delete(key);
      return undefined;
    }
    return value;
  }
}

// 使用示例
const cache = new WeakCache();
const user = { name: "张三" };
cache.set("user1", user);

console.log(cache.get("user1")); // 输出 { name: "张三" }

// 解除强引用,等待垃圾回收
// user = null;

使用限制和注意事项

  • 垃圾回收的触发时机是完全不确定的,你无法预测WeakRef指向的对象什么时候会被回收,也无法主动触发回收,因此不要依赖deref的结果来做关键逻辑判断。
  • FinalizationRegistry的回调执行时机也是不确定的,而且不同的JavaScript引擎可能有不同的实现,不要假设回调会在对象被回收后立即执行。
  • 不要在终结器的回调中访问被回收的对象,也不要在回调中做太复杂的操作,避免影响性能。
  • WeakRef和FinalizationRegistry都不适合用在需要精确控制内存的场景,它们只是提供了更灵活的内存管理方式,不能替代合理的内存使用习惯。

和其他弱引用类型的区别

JavaScript中还有WeakMap和WeakSet这两种弱引用集合,它们的键是弱引用,不会阻止键对象被回收。和WeakRef、FinalizationRegistry的区别如下:

类型作用适用场景
WeakMap键为弱引用的键值对集合,键被回收后对应条目自动移除给对象添加临时关联数据,不阻止对象回收
WeakSet元素为弱引用的集合,元素被回收后自动从集合中移除跟踪对象是否存在,不阻止对象回收
WeakRef创建单个对象的弱引用,可随时获取对象引用需要单独引用某个对象且不阻止其回收的场景
FinalizationRegistry对象被回收后执行清理回调对象回收后的资源释放、缓存清理等

总的来说,WeakRef和FinalizationRegistry为JavaScript的内存管理提供了更细粒度的控制能力,合理使用可以避免一些不必要的内存泄漏,提升应用的性能。但也要注意它们的不确定性,不要过度依赖这两个特性,优先还是要从代码结构层面做好内存管理。

WeakRefFinalizationRegistryJavaScript内存管理修改时间:2026-06-20 08:57:32

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。