前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JavaScript中的对象管理和事件清理

JavaScript中的对象管理和事件清理

原创
作者头像
zayyo
发布2024-02-15 22:29:34
1610
发布2024-02-15 22:29:34

JavaScript作为一种垃圾回收语言,通常我们不必关心对象的分配和释放问题。但偶尔,在处理回调函数时,即使不再有任何有意义的引用,也很容易让对象永远保持活跃状态。

语言为我们提供了几种工具来处理这些情况:

  • WeakRef:用于存储对对象的单个弱引用
  • WeakMap:只要对象存在,就将值与对象关联起来
  • WeakSet:只要对象存在,就将其记住
  • FinalizationRegistry:当对象被收集时执行某些操作

根据情况,我们可能需要这些功能中的一个或另一个,但我今天想描述的情况将使用第一个和最后一个功能。

一个常见的情况是对象关心某些外部状态的变化,只要它们存在就要关注。例如,自定义元素可能希望在window对象上监听"scroll"事件。但是,简单地向window添加事件侦听器意味着保留对对象的引用。如果这些自定义元素的生命周期很短但数量很多,它们将在内存中累积,并且额外的事件侦听器也会堆积并浪费处理能力。

以下是一个类似情况的简单示例:

代码语言:javascript
复制
class MyElement extends HTMLElement {
   constructor() {
      super()
      window.addEventListener("scroll", event => {
         this.handleScroll()
      })
   }

   handleScroll() {
      this.classList.toggle("top", window.scrollY == 0)
   }
}

我们希望在对象被垃圾回收时移除事件侦听器。为了实现这一点,我们可以利用两个特性:

首先,将事件侦听器中对this的强引用替换为WeakRef将阻止事件侦听器在没有其他引用存在时保持对象活跃。一旦对象被收集,deref()方法将返回undefined。

代码语言:javascript
复制
const ref = new WeakRef(this)
window.addEventListener("scroll", event => {
   ref.deref()?.handleScroll()
})

这将允许对象被垃圾回收,但将保留事件侦听器附加,这意味着它仍将在每个滚动事件上触发,无法解除引用并因此什么也不做。

清理事件侦听器的一种简单方法是将AbortController与FinalizationRegistry结合使用。

前者让我们向事件传递一个信号,该信号将删除事件,而后者允许我们在某些对象被收集时运行一些代码。

这个接口相对基本:我们创建一个新的FinalizationRegistry并传递一个回调。然后,我们注册一个对象A和一个关联的(不同的)对象B。当A被垃圾回收时,显然无法将其传递给回调,因此回调会传递B。

代码语言:javascript
复制
const abortRegistry = new FinalizationRegistry(c => c.abort())

现在,这个abortRegistry允许我们注册一个对象和一个关联的AbortController,并且每当对象被收集时,将调用controller的abort()方法。

现在我们只需要在创建时注册对象,并将控制器的信号传递给事件侦听器。

以下是完整的代码:

代码语言:javascript
复制
const abortRegistry = new FinalizationRegistry(c => c.abort())

class MyElement extends HTMLElement {
   constructor() {
      super()
      const ref = new WeakRef(this)
      const controller = new AbortController()
      abortRegistry.register(this, controller)

      window.addEventListener("scroll", event => {
         ref.deref()?.handleScroll()
      }, { signal: controller.signal })
   }

   handleScroll() {
      this.classList.toggle("top", window.scrollY == 0)
   }
}

我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档