Объект WeakMap
WeakMap
представляет развитие коллекции Map. Особенностью WeakMap
является то, что все ее элементы должны представлять объекты. При этом объектами должны быть как ключи, так и значения.
Создание WeakMap
:
// пустой WeakMap
const weakMap1 = new WeakMap();
// WeakMap с инициализацией данными
let key1 = {key:1};
let key2 = {key:2};
let value1 = {name: "Tom"};
let value2 = {name: "Sam"};
const weakMap2 = new WeakMap([[key1, value1], [key2, value2]]);
// или так const weakMap2 = new WeakMap([[{key:1}, {name: "Tom"}], [{key:2}, {name: "Sam"}]]);
Стоит отметить, что объект WeakMap не поддерживает перебор.
Для добавления новых объектов или изменения старых применяется метод set()
:
let key1 = {key:1};
let key2 = {key:2};
let value1 = {name: "Tom"};
let value2 = {name: "Sam"};
const weakMap2 = new WeakMap([[key1, value1]]);
weakMap2.set(key2, value2);
weakMap2.set(key1, {name: "Kate"});
// {name: "Kate"}
console.log(weakMap2.get(key1));
// {name: "Sam"}
console.log(weakMap2.get(key2));
Для получения объектов по ключу из WeakMap
применяется метод get()
:
let key1 = {key:1};
let key2 = {key:2};
let value1 = {name: "Tom"};
let value2 = {name: "Sam"};
const weakMap2 = new WeakMap([[key1, value1], [key2, value2]]);
// {name: "Tom"}
console.log(weakMap2.get(key1));
Чтобы проверить наличие элемента по определенному ключу, применяется метод has()
, который возвращает true
при наличии элемента:
let key1 = {key:1},
key2 = {key:2};
let value1 = {name: "Tom"},
value2 = {name: "Sam"};
const weakMap2 = new WeakMap([[key1, value1]]);
// true
console.log(weakMap2.has(key1));
// false
console.log(weakMap2.has(key2));
Для удаления элемента по ключу применяется метод delete()
:
let key1 = {key:1},
key2 = {key:2};
let value1 = {name: "Tom"},
value2 = {name: "Sam"};
const weakMap2 = new WeakMap([[key1, value1], [key2, value2]]);
// true
console.log(weakMap2.has(key1));
weakMap2.delete(key1);
// false
console.log(weakMap2.has(key1));
Слабые ссылки
Объекты передаются в WeakMap
по ссылке. И когда объект перестает существовать в силу различных причин, он удаляется из WeakMap
. Рассмотрим следующий пример:
let jsCode = {code: "js"},
tsCode = {code: "ts"};
let js = {lang: "JavaScript"},
ts = {lang: "TypeScript"};
const weakMap = new WeakMap([[jsCode, js], [tsCode, ts]]);
jsCode = null;
// WeakMap {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}}
console.log(weakMap);
console.log("Некоторая работа");
const timerId = setTimeout(function(){
// WeakMap {{code: "ts"} => {lang: "TypeScript"}}
console.log(weakMap);
clearTimeout(timerId);
}, 10000);
В данном случае сначала объект WeakMap
хранит ссылки на два элемента с ключами jsCode
и tsCode
. Далее для переменной jsCode
устанавливается значение null
:
jsCode = null;
Это приведет к тому, что спустя некоторое время начальное значение этой переменной будет удалено сборщиком мусора JavaScript. Причем если сразу после этого мы посмотрим на содержимое weakMap
, то увидим, что объект с ключом jsCode
в нем еще присутствует. Однако спустя некоторое время ссылка будет удалена из weakSet
. Для эмуляции прошествия времени здесь используется функция setTimeout
, которая выводит на консоль содержимое weakSet
через 10000 секунд (конкретный период времени, через который сборщик мусора удалит значение, может отличаться).
Теперь сравним с тем, что произойдет, если вместо WeakMap
использовать Map
:
let jsCode = {code: "js"},
tsCode = {code: "ts"};
let js = {lang: "JavaScript"},
ts = {lang: "TypeScript"};
const map = new Map([[jsCode, js], [tsCode, ts]]);
jsCode = null;
// Map(2) {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}}
console.log(map);
console.log("Некоторая работа");
const timerId = setTimeout(function(){
// Map(2) {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}}
console.log(map);
clearTimeout(timerId);
}, 10000);
В случае с Map
даже спустя некоторое время мы увидим, что в объекте Map
до сих пор присутствует объект, для которого было установлено значение null
.