FinalizationRegistry
Baseline Широко доступно
Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна в браузерах с апреля 2021 года.
Объект FinalizationRegistry позволяет запрашивать вызов функции обратного вызова при сборке мусора значения.
Описание
FinalizationRegistry предоставляет способ запросить вызов функции очистки в определенный момент, когда значение, зарегистрированное в реестре, было освобождено (собрано сборщиком мусора). (Функции очистки иногда называют финализаторами).
Примечание: Функции очистки не следует использовать для критически важной логики программы. Подробности см. в разделе Примечания к функциям очистки.
Вы создаете реестр, передавая ему функцию обратного вызова:
const registry = new FinalizationRegistry((heldValue) => {
// …
});
Затем вы регистрируете любое значение, для которого вы хотите получить функцию очистки, вызывая метод register, передавая ему значение и сохраненное значение для него:
registry.register(target, "some value");
Реестр не хранит сильную ссылку на значение, так как это сведет на нет его цель (если бы реестр хранил его сильно, значение никогда не было бы освобождено). В JavaScript объекты и незарегистрированные символы собираются сборщиком мусора, поэтому их можно зарегистрировать в объекте FinalizationRegistry в качестве цели или токена.
Если target будет освобождено, ваша функция очистки может быть вызвана в определенный момент с сохраненным значением, которое вы предоставили для нее ("some value" в примере выше). Сохраненное значение может быть любым значением: примитивом или объектом, даже undefined. Если сохраненное значение является объектом, реестр сохраняет на него сильную ссылку (чтобы передать его позже вашей функции очистки).
Если вы хотите иметь возможность отменить регистрацию зарегистрированного целевого значения позже, вы передаете третье значение, которое является токеном отмены регистрации, который вы будете использовать позже при вызове функции реестра unregister для отмены регистрации значения. Реестр хранит только слабую ссылку на токен отмены регистрации.
Часто используется само целевое значение в качестве токена отмены регистрации, что вполне допустимо:
registry.register(target, "some value", target); // … // some time later, if you don't care about `target` anymore, unregister it registry.unregister(target);
Однако это не обязательно должно быть то же самое значение; это может быть другое значение:
registry.register(target, "some value", token); // … // some time later registry.unregister(token);
По возможности избегайте
Правильное использование FinalizationRegistry требует тщательного обдумывания, и его лучше избегать, если это возможно. Также важно избегать зависимости от каких-либо конкретных поведений, не гарантированных спецификацией. Когда, как и происходит ли сборка мусора, зависит от реализации любого данного движка JavaScript. Любое поведение, которое вы наблюдаете в одном движке, может отличаться в другом движке, в другой версии того же движка или даже в немного другой ситуации с той же версией того же движка. Сборка мусора — сложная задача, над решениями которой разработчики движков JavaScript постоянно работают и совершенствуют их.
Вот некоторые конкретные моменты, включенные авторами в предложение, которое ввело FinalizationRegistry:
Сборщики мусора сложны. Если приложение или библиотека зависят от того, что сборщик мусора своевременно и предсказуемо освобождает WeakRef или вызывает финализатор [функцию очистки], оно, вероятно, будет разочаровано: очистка может произойти гораздо позже, чем ожидалось, или не произойти вообще. Источники вариативности включают:
- Один объект может быть собран гораздо раньше другого, даже если они стали недоступными одновременно, например, из-за поколенной сборки.
- Работа по сборке мусора может быть распределена во времени с использованием инкрементных и параллельных методов.
- Могут использоваться различные эвристики среды выполнения для балансировки использования памяти и отзывчивости.
- Движок JavaScript может хранить ссылки на объекты, которые кажутся недоступными (например, в замыканиях или встроенных кэшах).
- Различные движки JavaScript могут делать это по-разному, или один и тот же движок может изменять свои алгоритмы в разных версиях.
- Сложные факторы могут привести к тому, что объекты остаются активными неожиданно долго, например, при использовании с определенными API.
Примечания к функциям очистки
- Разработчики не должны полагаться на функции очистки для критически важной логики программы. Функции очистки могут быть полезны для снижения использования памяти в течение выполнения программы, но вряд ли будут полезны в других случаях.
- Если ваш код только что зарегистрировал значение в реестре, это целевое значение не будет освобождено до конца текущей задачи JavaScript. См. примечания к WeakRefs для подробностей.
- Совместимая реализация JavaScript, даже та, которая выполняет сборку мусора, не обязана вызывать функции очистки. Когда и будет ли она это делать, полностью зависит от реализации движка JavaScript. Когда зарегистрированный объект будет освобожден, любые связанные с ним функции очистки могут быть вызваны тогда, или позже, или не вызваны вообще.
- Вероятно, основные реализации вызовут функции очистки в определенный момент выполнения, но эти вызовы могут быть значительно позже, чем был освобожден связанный объект. Кроме того, если объект зарегистрирован в двух реестрах, нет гарантии, что два вызова функции очистки будут следовать друг за другом — один может быть вызван, а другой никогда не вызван, или другой может быть вызван гораздо позже.
- Существуют также ситуации, когда даже реализации, которые обычно вызывают функции очистки, вряд ли их вызовут:
- При полном завершении работы программы JavaScript (например, при закрытии вкладки в браузере).
- Когда сам экземпляр
FinalizationRegistryбольше не доступен коду JavaScript.
- Если целевой объект
WeakRefтакже находится вFinalizationRegistry, целевой объектWeakRefочищается одновременно или до вызова любой функции очистки, связанной с реестром; если ваша функция очистки вызываетderefдляWeakRefдля объекта, она получитundefined.
Конструктор
-
FinalizationRegistry() - Создает новый объект
FinalizationRegistry.
Свойства экземпляра
Эти свойства определены в FinalizationRegistry.prototype и используются всеми экземплярами FinalizationRegistry.
-
FinalizationRegistry.prototype.constructor - Конструкторская функция, которая создала объект экземпляра. Для экземпляров
FinalizationRegistryначальным значением является конструкторFinalizationRegistry. -
FinalizationRegistry.prototype[Symbol.toStringTag] - Начальным значением свойства
[Symbol.toStringTag]является строка"FinalizationRegistry". Это свойство используется вObject.prototype.toString().
Методы экземпляра
-
FinalizationRegistry.prototype.register() - Регистрирует объект в реестре, чтобы получить функцию очистки, когда/если объект будет собран сборщиком мусора.
-
FinalizationRegistry.prototype.unregister() - Отменяет регистрацию объекта из реестра.
Примеры
Создание нового реестра
Вы создаете реестр, передавая ему функцию обратного вызова:
const registry = new FinalizationRegistry((heldValue) => {
// …
});
Регистрация объектов для очистки
Затем вы регистрируете любые объекты, для которых вы хотите получить функцию очистки, вызывая метод register, передавая ему объект и сохраненное значение для него:
registry.register(theObject, "some value");
Функции обратного вызова никогда не вызываются синхронно
Независимо от того, какое давление вы оказываете на сборщик мусора, функция очистки никогда не будет вызвана синхронно. Объект может быть освобожден синхронно, но функция обратного вызова всегда будет вызвана после завершения текущей задачи:
let counter = 0;
const registry = new FinalizationRegistry(() => {
console.log(`Array gets garbage collected at ${counter}`);
});
registry.register(["foo"]);
(function allocateMemory() {
// Allocate 50000 functions — a lot of memory!
Array.from({ length: 50000 }, () => () => {});
if (counter > 5000) return;
counter++;
allocateMemory();
})();
console.log("Main job ends");
// Logs:
// Main job ends
// Array gets garbage collected at 5001
Однако, если вы позволите небольшую паузу между каждой аллокацией, функция обратного вызова может быть вызвана раньше:
let arrayCollected = false;
let counter = 0;
const registry = new FinalizationRegistry(() => {
console.log(`Array gets garbage collected at ${counter}`);
arrayCollected = true;
});
registry.register(["foo"]);
(function allocateMemory() {
// Allocate 50000 functions — a lot of memory!
Array.from({ length: 50000 }, () => () => {});
if (counter > 5000 || arrayCollected) return;
counter++;
// Use setTimeout to make each allocateMemory a different job
setTimeout(allocateMemory);
})();
console.log("Main job ends");
Нет гарантии, что функция обратного вызова будет вызвана раньше или вообще будет вызвана, но есть вероятность, что у выведенного сообщения будет значение счетчика меньше 5000.
Спецификации
Совместимость с браузерами
| Desktop | Mobile | Server | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox for Android | Opera Android | Safari on iOS | Samsung Internet | WebView Android | WebView on iOS | Bun | Deno | Node.js | |
FinalizationRegistry |
84 |
84 |
79 |
70 |
14.1 |
84 |
79 |
60 |
14.5 |
14.0 |
84 |
14.5 |
1.0.0 |
1.0 |
14.6.0 |
FinalizationRegistry |
84 |
84 |
79 |
70 |
14.1 |
84 |
79 |
60 |
14.5 |
14.0 |
84 |
14.5 |
1.0.0 |
1.0 |
14.6.0 |
register |
84 |
84 |
79 |
70 |
14.1 |
84 |
79 |
60 |
14.5 |
14.0 |
84 |
14.5 |
1.0.0 |
1.0 |
14.6.0 |
unregister |
84 |
84 |
79 |
70 |
14.1 |
84 |
79 |
60 |
14.5 |
14.0 |
84 |
14.5 |
1.0.0 |
1.0 |
14.6.0 |
См. также
© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry