Skip to main content

Що робить WeakRef?

Що таке WeakRef

WeakRef (Weak Reference) - це об'єкт, який містить слабке посилання на інший об'єкт, тобто таке посилання, яке не заважає збирачу сміття видалити цей об'єкт, якщо на нього більше ніхто не посилається.

Простіше кажучи:

WeakRef дозволяє "пам'ятати" об'єкт поки він живий, але не утримувати його в пам'яті штучно.

Приклад звичайного (сильного) посилання

javascript
let user = { name: "Tim" }; let ref = user; // звичайне посилання user = null; // але ref все ще утримує об'єкт! console.log(ref.name); // "Tim"

Об'єкт не буде видалений збирачем сміття, поки існує хоча б одне "сильне" посилання (ref).

Приклад слабкого посилання (WeakRef)

javascript
let user = { name: "Tim" }; let weakRef = new WeakRef(user); user = null; // тепер немає сильних посилань // ...пізніше, можливо... console.log(weakRef.deref()?.name); // "Tim" або undefined

Тепер об'єкт може бути видалений GC у будь-який момент, а weakRef.deref() поверне undefined, якщо об'єкт уже зібрано.

Як працює 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 більше не досяжний напряму setTimeout(() => { console.log(ref.deref()); // або об'єкт, або undefined - залежить від GC }, 1000);

Збирач сміття запускається у невизначений момент. Не можна передбачити, коли deref() почне повертати undefined.

Навіщо потрібен WeakRef

Зазвичай у JS не потрібно дбати про ручне керування пам'яттю. Але є вузькі випадки, коли 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 видалити його, поки кеш не очиститься вручну.

Рішення - використовувати WeakRef та FinalizationRegistry:

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(`Об'єкт ${id} видалено з пам'яті`); }); let user = { name: "Tim" }; registry.register(user, "user#1"); let ref = new WeakRef(user); user = null;

Коли GC збере user, викличеться callback:

javascript
Об'єкт user#1 видалено з пам'яті

(виклик не гарантований за часом і може взагалі не відбутись перед завершенням процесу.)

Важливо знати

ОсобливістьОпис
WeakRef не запобігає збираннюоб'єкт може зникнути в будь-який момент
deref() може повернути undefinedякщо GC уже видалив об'єкт
Не можна "покладатись" на час життяGC непередбачуваний
Не використовувати без потребиможе призвести до "зомбі"-помилок (об'єкт зник раптово)
Корисний лише у спец. сценаріяхкеш, мемоізація, інтеграція з GC

Коли не варто використовувати WeakRef

Для керування звичайними даними. Щоб "пришвидшити" GC - це марно. Для зберігання станів у React/Vue/Next. У критичних логічних ланцюжках (результат може зникнути в будь-який момент).

Підсумок

Що робитьСтворює слабке посилання на об'єкт
Як працюєНе заважає GC видалити об'єкт
Метод.deref() повертає об'єкт або undefined
Корисний дляКешів, мемоізації, трекінгу пам'яті
РизикиОб'єкт може зникнути в будь-який момент
З'явився уES2021

Головна ідея:

WeakRef = "посилання, яке не заважає збирачу сміття". Використовуй, коли хочеш "тримати об'єкт, але не володіти ним".

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.