Что такое Weak Reference (слабая ссылка) в JavaScript?

Ответ

Weak Reference (слабая ссылка) — это ссылка на объект, которая не защищает этот объект от сборки мусора (Garbage Collection). Если на объект остались только слабые ссылки, сборщик мусора может удалить его, и слабые ссылки автоматически станут undefined (или коллекция их очистит).

В JavaScript слабые ссылки реализованы через две встроенные коллекции: WeakMap и WeakSet.

Ключевые отличия от Map и Set:

  1. Ключами могут быть только объекты (не примитивы).
  2. Нет итераторов. Нельзя получить список всех ключей или значений (keys(), values(), entries()), нет свойства size. Это связано с тем, что перебор зависит от состояния сборки мусора, которое недетерминировано.
  3. Автоматическая очистка. Если объект-ключ удалён сборщиком мусора, запись автоматически удаляется из WeakMap, а объект — из WeakSet.

Практическое применение WeakMap: Часто используется для хранения приватных или вспомогательных данных объекта, не вмешиваясь в его жизненный цикл и не вызывая утечек памяти.

// Пример: хранение приватных метаданных для объектов
const privateData = new WeakMap();

class User {
    constructor(name) {
        // this - объект, используем его как ключ в WeakMap
        privateData.set(this, { 
            name: name,
            secretToken: Math.random().toString(36).substr(2) 
        });
    }

    getName() {
        // Получаем данные по ссылке на текущий экземпляр
        return privateData.get(this).name;
    }
}

let user = new User('Анна');
console.log(user.getName()); // 'Анна'
console.log(privateData.get(user)); // { name: 'Анна', secretToken: '...' }

// Когда объект user будет удалён (например, вышел из области видимости),
// запись в WeakMap также будет автоматически удалена сборщиком мусора.
// Это предотвращает утечку памяти.
user = null; 
// Теперь privateData не удерживает старый объект в памяти.

WeakSet используется аналогично для хранения наборов объектов (например, для отслеживания, был ли объект обработан), не препятствуя их удалению.

Ответ 18+ 🔞

Давай разберём эту тему, а то некоторые до сих пор думают, что WeakMap — это карта для слабаков. Ёпта, нет же.

Смотри, представь обычную ссылку на объект — это как железная хватка. Пока ты за него держишься, сборщик мусора (этот дворник памяти) мимо пройдёт и подумает: «А, жилой, окей». А слабая ссылка — это как взять того же чувака за рукав, но так, не особо цепко. Если он сам решит свалить (или его другие железные хватки отпустят), то он свалит, а твоя рука просто повиснет в воздухе, и ссылка станет undefined. Вот и вся магия. Объект могут почистить, если на него только слабые ссылки остались.

В JavaScript это реализовано в двух коллекциях: WeakMap и WeakSet. И у них, в отличие от их тупых братьев Map и Set, есть три главные фишки, от которых терпения ноль ебать у тех, кто ждёт от них обычного поведения:

  1. Ключи — только объекты. Не суй туда строки, числа или свою надежду на светлое будущее. Только объекты. Иначе получишь чих-пых тебя в сраку, то есть ошибку.
  2. Нет итераторов и size. Нельзя пройтись forEach, нельзя получить keys(). Доверия ебать ноль у движка к состоянию сборки мусора, оно нестабильное. Поэтому он тебе и не даёт эти методы, чтобы ты не охуел, когда ключи на половине перебора вдруг испарятся.
  3. Автоматическая зачистка. Это главная фишка. Удалили объект из памяти — запись из WeakMap или WeakSet сама исчезнет. Никаких утечек памяти, красота.

Зачем этот WeakMap вообще нужен? Чаще всего — чтобы привязать к объекту какие-то приватные данные, но так, чтобы не мешать ему умирать. Классический пример — эмуляция приватных полей в классе, когда тебе подозрение ебать чувствую, что лезть в публичные свойства — плохая идея.

Смотри, как это выглядит на практике:

// Создаём наш тайный склад данных. Это WeakMap.
const secretStorage = new WeakMap();

class Person {
    constructor(name) {
        // this — это и есть сам объект (например, {Person: ...}).
        // Используем его как КЛЮЧ в нашей тайной карте.
        // А значением кладём объект с данными, которые хотим спрятать.
        secretStorage.set(this, {
            name: name,
            secretCode: 'ХУЙ' + Math.random() // Ну или что-то посерьёзнее
        });
    }

    getName() {
        // Чтобы прочитать приватные данные, просто достаём их по ключу this.
        // То есть по ссылке на текущий экземпляр.
        return secretStorage.get(this).name;
    }
}

let person = new Person('Василий');
console.log(person.getName()); // 'Василий'
console.log(secretStorage.get(person)); // { name: 'Василий', secretCode: 'ХУЙ0.abc123' }

// А теперь магия! Убиваем ссылку на объект.
person = null;
// Ждём сборки мусора (в реальности не сразу, но факт)...
// И запись, связанная с УДАЛЁННЫМ объектом, АВТОМАТИЧЕСКИ исчезнет из secretStorage!
// Память почистилась, утечки нет. Это и есть **овердохуища** полезно.

А WeakSet? С ним ещё проще. Это как клуб по интересам, куда записывают объекты. Используют, чтобы отмечать, что объект уже был обработан (например, посещённая нода в графе). И если объект удалили из программы, он автоматом выбывает из клуба. Удобно, я тебе скажу.

Короче, суть в чём: используй WeakMap/WeakSet, когда нужно привязать данные или метку к жизни объекта, но сам от себя охуел бы от мысли, что из-за твоих данных объект будет вечно висеть в памяти. Для всего остального — обычные Map и Set.