WeakRef
WeakRef 对象允许您保留对另一个对象的弱引用,而不会阻止被弱引用对象被 GC 回收
实例方法
返回当前实例的 WeakRef 对象所绑定的 target 对象,如果该 target 对象已被 GC 回收则返回undefined
正确使用 WeakRef 对象需要仔细的考虑,最好尽量避免使用。
- 如果您的代码刚刚为目标对象创建了
WeakRef,或者从WeakRef的deref方法获取了目标对象,在当前 JavaScript job (包括在脚本作业末尾运行的任何 promise reaction 作业) 结束之前,不会回收该目标对象 - 如果多个
WeakRefs有相同的目标,那么他们的target对象是一样的。 - 如果一个对象是
WeakRef的target,又在FinalizationRegistry,那么该target就会在调用与注册表关联的任何清理回调之前或者同时被清理。如果清理回调调用对象的WeakRef上的deref,它将收到undefined - 你不能更改
WeakRef的target,它将始终是第一次指定的target或者在回收该target时会定义 WeakRef可能永远不会从deref返回undefined,即使没有什么东西能很好地保存target,因为GC可能永远不会决定回收对象。
用途
缓存数据
// 缓存
function makeWeakCached(f) {
const cache = new Map();
return key => {
const ref = cache.get(key);
if (ref) {
const cached = ref.deref();
if (cached !== undefined) return cached;
}
const fresh = f(key);
cache.set(key, new WeakRef(fresh));
return fresh;
};
}
const getImageCached = makeWeakCached(getImage);
FinalizationRegistry
FinalizationRegistry (清理器注册表) 对象可以让你在对象被垃圾回收时请求一个回调。
清理回调不应被用于必要的程序逻辑
正确使用需要仔细考虑,如果可能的话,最好避免使用。
const registry = new FinalizationRegistry((heldValue) => {
// ....
});
实例方法
-
FinalizationRegistry.prototype.register(target, heldValue, unregisterToken)-
target
要注册的目标值
-
heldValue
传递给清理回调函数的参数。不可以是
target -
unregisterToken
unregister 方法使用以注销目标值。必须是对象或未注册的
Symbol。如果未提供,则无法注销目标。
-
-
FinalizationRegistry.prototype.unregister(unregisterToken)注销目标
class Thingy { static #cleanup = (label) => { // ^^^^^−−−−− held value console.error( `The "release" method was never called for the object with the label "${label}"`, ); }; #registry = new FinalizationRegistry(Thingy.#cleanup); /** * Constructs a `Thingy` instance. * Be sure to call `release` when you're done with it. * * @param label A label for the `Thingy`. */ constructor(label) { // vvvvv−−−−− held value this.#registry.register(this, label, this); // target −−−−−^^^^ ^^^^−−−−− unregister token } /** * Releases resources held by this `Thingy` instance. */ release() { this.#registry.unregister(this); // ^^^^−−−−− unregister token } } class Thingy { static #cleanup = (file) => { // ^^^^−−−−− held value console.error( `The "release" method was never called for the "Thingy" for the file "${file.name}"`, ); }; #registry = new FinalizationRegistry(Thingy.#cleanup); #file; /** * Constructs a `Thingy` instance for the given file. * Be sure to call `release` when you're done with it. * * @param filename The name of the file. */ constructor(filename) { this.#file = File.open(filename); // vvvvv−−−−− held value this.#registry.register(this, label, this.#file); // target −−−−−^^^^ ^^^^^^^^^^−−−−− unregister token } /** * Releases resources held by this `Thingy` instance. */ release() { if (this.#file) { this.#registry.unregister(this.#file); // ^^^^^^^^^^−−−−− unregister token File.close(this.#file); this.#file = null; } } }class Thingy { static #cleanup = (file) => { // ^^^^−−−−− held value console.error( `The "release" method was never called for the "Thingy" for the file "${file.name}"`, ); }; #registry = new FinalizationRegistry(Thingy.#cleanup); #file; /** * Constructs a `Thingy` instance for the given file. * Be sure to call `release` when you're done with it. * * @param filename The name of the file. */ constructor(filename) { this.#file = File.open(filename); // vvvvv−−−−− held value this.#registry.register(this, label, this.#file); // target −−−−−^^^^ ^^^^^^^^^^−−−−− unregister token } /** * Releases resources held by this `Thingy` instance. */ release() { if (this.#file) { this.#registry.unregister(this.#file); // ^^^^^^^^^^−−−−− unregister token File.close(this.#file); this.#file = null; } } }
用途
缓存
function makeWeakCached(f) {
const cache = new Map();
const cleanup = new FinalizationRegistry(key => {
const ref = cache.get(key);
if (ref && !ref.deref()) cache.delete(key);
});
return key => {
const ref = cache.get(key);
if (ref) {
const cached = ref.deref();
if (cached !== undefined) return cached;
}
const fresh = f(key);
cache.set(key, new WeakRef(fresh));
cleanup.register(fresh, key);
return fresh;
};
}
const getImageCached = makeWeakCached(getImage);