在 JavaScript 中,闭包是一个非常强大的概念,它可以允许我们创建一些非常复杂的行为和逻辑,但也容易导致内存泄漏和不必要的资源消耗。ES2020 增加了一些新的功能来更好地控制闭包,使其更加安全和可靠。
基本概念
在 JavaScript 中,闭包是一个函数和其周围的状态(词法环境)形成的一个作用域的组合。当函数访问其周围的状态时,就形成了一个闭包。例如:
-- -------------------- ---- ------- -------- --------- - --- ----- - -- ------ ---------- - -------- ------------------- - - ----- --------- - ---------- ------------ -- -- - ------------ -- -- - ------------ -- -- -
在这个例子中,counter
函数内部定义了一个变量 count
并返回了一个匿名函数,该函数每次被调用时都会将 count
的值加一并打印出来。因为匿名函数可以访问 counter
函数的 count
变量,所以每次调用 myCounter
函数时都会对 count
的值进行更新。
更好地控制闭包
虽然闭包可以让我们创建强大的功能,但也容易导致内存泄漏和不必要的资源消耗。因此,ES2020 增加了 WeakRef
、FinalizationRegistry
和 WeakMap
等功能,使我们能够更好地控制闭包。
WeakRef
WeakRef
是 ES2020 中的一个新 API,它允许我们创建一个对对象的弱引用,当对象被垃圾回收时,弱引用将自动被删除。这可以帮助我们避免内存泄漏和不必要的资源消耗。例如:
-- -------------------- ---- ------- ----- --------------- - ------------- - --------- - ------------------ -------------------- --------------- - --------- - ---------------------- --------------- - - ----- -------- - --- ------------------ ----- --------- - --- ------------------ -------- - ----- -- --------------
在这个例子中,我们创建了一个 ExpensiveObject
类,它在创建时会输出一条消息,然后我们通过 WeakRef
创建了一个对 myObject
的弱引用。接着,我们将 myObject
设置为 null
,这样 ExpensiveObject
就没有被任何变量引用了。如果我们等待一段时间,让垃圾回收器运行,你将会看到 ExpensiveObject
的 destroy
方法被调用了,证明其已经被成功清除。
FinalizationRegistry
FinalizationRegistry
可以帮助我们在对象被垃圾回收时执行某些操作,例如清理与其相关的资源。例如:
-- -------------------- ---- ------- ----- --------------- - ------------- - --------- - ------------------ -------------------- --------------- - --------- - ---------------------- --------------- - - ----- -------- - --- ------------------------ -- - -------------- --- --- -------- - --- ------------------ --------------------------- ---------- -- --------------
在这个例子中,我们定义了一个 ExpensiveObject
类和一个 FinalizationRegistry
的实例,然后创建了一个 myObject
的实例并使用 registry
的 register
方法注册它。接着,我们将 myObject
设置为 null
,让其成为垃圾对象。当垃圾回收器回收 myObject
时,会调用 registry
的回调函数,并将 myObject
作为参数传递给它。在回调函数中,我们调用 destroy
方法来清理与 ExpensiveObject
相关的资源。
WeakMap
WeakMap
是 ES2015 中引入的一个新数据结构,它允许我们使用对象作为键,而不必担心键被垃圾回收。在 JavaScript 中,对象键的一个常见问题是它们容易导致内存泄漏,因为即使该对象本身已经成为垃圾对象,但该对象在 Map 中的引用可能会造成其无法被垃圾回收。例如:
-- -------------------- ---- ------- ----- ----- - --- ------ ----- -------- - - ---- ------- -- ------------------- ----- -------- --------------------------------- -- -- ----- ------ -------- - ----- -- -------------
在这个例子中,我们使用 myObject
作为一个 Map
的键,并设置了其对应的值。然后,我们将 myObject
设置为 null
,这样它就变成了垃圾对象。但是,由于 myMap
中仍然有对 myObject
的引用,所以它将无法被垃圾回收。如果我们等待一段时间并观察内存消耗,你将会看到内存中仍然保留着 myMap
。
WeakMap
可以解决这个问题,因为它的键是弱引用,只要键对象不再被引用,它就会被自动从 Map 中删除。例如:
-- -------------------- ---- ------- ----- --------- - --- ---------- --- -------- - --- ----------------------- ----- -------- ------------------------------------- -- -- ----- ------ -------- - ----- -- --------------
在这个例子中,我们使用 WeakMap
来存储 myObject
的键值对。当我们将 myObject
设置为 null
时,它变成了垃圾对象,因此 myWeakMap
中也就没有对它的强引用了。如果我们等待一段时间,并观察控制台输出,你将会发现 myWeakMap
已经不再拥有对 myObject
的引用了。
总结
在本文中,我们介绍了 ES2020 中用于更好地控制闭包的一些新功能,包括 WeakRef
、FinalizationRegistry
和 WeakMap
。这些 API 帮助我们在使用闭包时更加安全和可靠,避免内存泄漏和不必要的资源消耗。通过熟练掌握这些技术,我们可以写出更加高效和可维护的 JavaScript 代码。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6455a108968c7c53b09126cf