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"}); console.log(weakMap2.get(key1)); //{name: "Kate"} console.log(weakMap2.get(key2)); //{name: "Sam"}
Для получения объектов по ключу из 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]]); console.log(weakMap2.get(key1)); // {name: "Tom"}
Чтобы проверить наличие элемента по определенному ключу, применяется метод has(), который возвращает true при наличии элемента:
let key1 = {key:1}, key2 = {key:2}; let value1 = {name: "Tom"}, value2 = {name: "Sam"}; const weakMap2 = new WeakMap([[key1, value1]]); console.log(weakMap2.has(key1)); // true console.log(weakMap2.has(key2)); // false
Для удаления элемента по ключу применяется метод delete():
let key1 = {key:1}, key2 = {key:2}; let value1 = {name: "Tom"}, value2 = {name: "Sam"}; const weakMap2 = new WeakMap([[key1, value1], [key2, value2]]); console.log(weakMap2.has(key1)); // true weakMap2.delete(key1); console.log(weakMap2.has(key1)); // false
Объекты передаются в WeakMap по ссылке. И отличительной особенностью 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; console.log(weakMap); // WeakMap {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}} console.log("Некоторая работа"); const timerId = setTimeout(function(){ console.log(weakMap); // WeakMap {{code: "ts"} => {lang: "TypeScript"}} clearTimeout(timerId); }, 30000);
В данном случае сначала объект 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; console.log(map); // Map(2) {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}} console.log("Некоторая работа"); const timerId = setTimeout(function(){ console.log(map); // Map(2) {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}} clearTimeout(timerId); }, 30000);
В случае с Map даже спустя некоторое время мы увидим, что в объекте Map до сих пор присутствует объект, для которого было установлено значение null