Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що робить WeakRef». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`WeakRef` це об'єкт зі слабким посиланням на інший об'єкт, тобто таким посиланням, яке не заважає збирачу сміття (GC) видалити цей об'єкт, якщо на нього більше ніхто не посилається сильно.** Створюють його через `new WeakRef(obj)`, а дістають значення методом `.deref()`, який повертає або оригінальний об'єкт, або `undefined`, якщо GC його вже зібрав. Момент збирання непередбачуваний, тому покладатися на час життя не можна. Практичні сценарії вузькі: кеш і мемоізація без витоків пам'яті, спостерігачі, які не мають утримувати об'єкт, кешування важких ресурсів. Разом із `FinalizationRegistry` можна дізнатися, що об'єкт уже зібрано, хоча цей callback теж не гарантований за часом. `WeakRef` з'явився в ES2021. ```javascript let user = { name: 'Tim' }; const weakRef = new WeakRef(user); user = null; // no strong references left console.log(weakRef.deref()?.name); // 'Tim' or undefined, depending on the GC ``` **Ключове:** `WeakRef` дозволяє «пам'ятати» об'єкт, поки він живий, але не утримувати його в пам'яті штучно.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`WeakRef` (weak reference) це об'єкт, який містить слабке посилання на інший об'єкт, тобто посилання, що не заважає збирачу сміття видалити цей об'єкт.** Простіше кажучи, `WeakRef` дозволяє «пам'ятати» об'єкт, поки він живий, але не утримувати його в пам'яті штучно. ## Теорія ### TL;DR - `new WeakRef(obj)` створює слабке посилання на `obj`. - `weakRef.deref()` повертає об'єкт або `undefined`, якщо GC його вже зібрав. - Слабке посилання **не заважає** збиранню сміття, на відміну від звичайного. - Момент запуску GC непередбачуваний, тому час життя об'єкта гарантувати не можна. - Корисний у вузьких сценаріях: кеш без витоків, мемоізація, спостерігачі, важкі ресурси. - `FinalizationRegistry` дає callback, коли об'єкт зібрано, але без гарантій за часом. - Доданий у ES2021; у звичайному коді потреби в ньому зазвичай немає. ### Швидкий приклад ```javascript let user = { name: 'Tim' }; const weakRef = new WeakRef(user); user = null; // there are no strong references any more // ...later, maybe... console.log(weakRef.deref()?.name); // 'Tim' or undefined ``` Тепер об'єкт може бути видалений GC будь-якої миті, і тоді `weakRef.deref()` поверне `undefined`. ### Сильні та слабкі посилання Звичайне (сильне) посилання утримує об'єкт у пам'яті: ```javascript let user = { name: 'Tim' }; let ref = user; // an ordinary, strong reference user = null; // but ref still holds the object console.log(ref.name); // 'Tim' ``` Об'єкт **не буде видалений** збирачем сміття, поки існує хоча б одне сильне посилання (`ref`). Слабке посилання поводиться інакше: воно не рахується при вирішенні того, чи об'єкт ще потрібен. ### Як працює `WeakRef` 1. `new WeakRef(obj)` створює слабке посилання. 2. `weakRef.deref()` намагається повернути оригінальний об'єкт, якщо він ще живий. 3. Якщо об'єкт уже зібрано GC, `deref()` поверне `undefined`. Реальна поведінка виглядає так: ```javascript let ref; (() => { let bigObject = { data: 'Some heavy data...' }; ref = new WeakRef(bigObject); })(); // bigObject is no longer reachable directly setTimeout(() => { console.log(ref.deref()); // either the object or undefined, up to the GC }, 1000); ``` Збирач сміття запускається у невизначений момент. Передбачити, коли саме `deref()` почне повертати `undefined`, неможливо. ### Навіщо потрібен `WeakRef` Зазвичай у JavaScript не треба дбати про ручне керування пам'яттю, але є вузькі випадки, де `WeakRef` корисний: | Сценарій | Приклад | | --- | --- | | **Кешування без витоків** | Хочемо кешувати об'єкти, але не тримати їх у пам'яті «вічно» | | **Мемоізація** | Зберігати результат обчислення для об'єкта, поки той живий | | **Реактивні системи та спостерігачі** | Стежити за об'єктом, але не заважати його збиранню | | **Ігрові рушії та графіка** | Кешувати важкі ресурси (текстури, дані), але дозволяти GC їх очищати | | **Інтеграція з FinalizationRegistry** | Реагувати, коли об'єкт зібрано GC | Звичайний `Map` утримує ключі сильно: ```javascript const cache = new Map(); function getUserData(user) { if (!cache.has(user)) { cache.set(user, heavyCompute(user)); } return cache.get(user); } ``` Проблема в тому, що навіть коли `user` більше не потрібен, `Map` не дасть GC його видалити, доки кеш не очистять вручну. Рішення, зберігати у кеші слабке посилання: ```javascript const cache = new Map(); function getUserData(user) { const ref = cache.get(user); let data = ref?.deref(); if (!data) { data = heavyCompute(user); cache.set(user, new WeakRef(data)); } return data; } ``` Тепер, якщо `data` більше ніде не використовується, GC її звільнить, і `ref.deref()` просто поверне `undefined`. ### Зв'язка з `FinalizationRegistry` Щоб дізнатися, **коли об'єкт було зібрано GC**, використовують `FinalizationRegistry`: ```javascript const registry = new FinalizationRegistry(id => { console.log(`Object ${id} was removed from memory`); }); let user = { name: 'Tim' }; registry.register(user, 'user#1'); let ref = new WeakRef(user); user = null; ``` Коли GC збере `user`, викличеться callback: ```text Object user#1 was removed from memory ``` Важливо: виклик не гарантований за часом і може взагалі не статися до завершення процесу. ### Коли не варто використовувати `WeakRef` Не використовуйте його: - для керування звичайними даними; - щоб «пришвидшити» GC, це марно; - для зберігання стану в React, Vue чи Next.js; - у критичних логічних ланцюжках, де результат не може раптово зникнути. Що треба пам'ятати: | Особливість | Опис | | --- | --- | | `WeakRef` не запобігає збиранню | Об'єкт може зникнути будь-якої миті | | `deref()` може повернути `undefined` | Якщо GC уже видалив об'єкт | | Не можна покладатися на час життя | GC непередбачуваний | | Не використовувати без потреби | Може призвести до «зомбі»-помилок, коли об'єкт зник раптово | | Корисний лише у спеціальних сценаріях | Кеш, мемоізація, інтеграція з GC | Підсумок: | Питання | Відповідь | | --- | --- | | Що робить | Створює слабке посилання на об'єкт | | Як працює | Не заважає GC видалити об'єкт | | Метод | `.deref()` повертає об'єкт або `undefined` | | Корисний для | Кешів, мемоізації, відстеження пам'яті | | Ризики | Об'єкт може зникнути будь-якої миті | | З'явився в | ES2021 | ### Типові помилки - **Очікують, що `deref()` завжди поверне об'єкт.** Він може повернути `undefined`; завжди перевіряйте результат. - **Кешують результат `deref()` у довгоживучій змінній.** Це створює сильне посилання і зводить нанівець сенс `WeakRef`. - **Плутають `WeakRef` з `WeakMap`.** `WeakMap` робить слабкими саме ключі і не дає їх перебирати, `WeakRef` це одне посилання на одне значення. - **Розраховують на `FinalizationRegistry` як на деструктор.** Callback може не спрацювати взагалі, тому звільняти файли чи сокети через нього не можна. - **Використовують `WeakRef`, щоб «допомогти» збирачу сміття.** GC працює за власним розкладом, слабке посилання його не пришвидшує. - **Тестують поведінку без урахування непередбачуваності GC.** Тест, який чекає `undefined` після `setTimeout`, буде нестабільним.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.