Эффективное или частное хранение данных с помощью JavaScript WeakMaps

Новая структура данных под названием WeakMaps была представлена с в новой спецификации ES6 наряду с Maps.

Как и в Maps, данные в WeakMaps хранятся в виде пар ключ-значение. 

Разница между ними в том, что WeakMaps не мешает сборке мусора, поскольку после уничтожения ссылок на объекты запись с уничтоженным объектом в качестве ключа становится недоступной, и его мусор собран. 

Применение WeakMaps

Поскольку запись уничтожается, как только уничтожается объект, являющийся ключом, WeakMap более эффективна, чем Maps, и обеспечивает частность, так как только код, ссылающийся на объект, может получить доступ к записи, хранящейся в WeakMap.

Следовательно, у WeakMaps есть несколько применений: 

  • Сохранение частного доступа к данным о конкретном объекте, разрешение доступа к значению только с объектом. 
  • Сохранение данных без использования дополнительных ресурсов. Сохранение временных данных. 
  • Хранение пар ключ-значение, которые можно уничтожить по ходу работы.

Определение WeakMaps

WeakMaps можно определить так: 

let jane= {
  name: "John"
};
let weakMap = new WeakMap([
  [jane, 1]
]);

Затем получаем доступ к записи с помощью ссылки на объект с методом get:

console.log(weakMap.get(jane));

Ключи в WeakMap могут быть только объектами. Если мы попробуем создать запись с примитивным значением, мы получим ошибку. Например, при попытке запустить: 

let weakMap = new WeakMap([
  [1, 1]
]);

Мы получим ошибку: ‘Uncaught TypeError: Invalid value used as weak map key.’

Как только ссылка на объект уничтожена, мы больше не сможем получить доступ к паре ключ-значение, ключом которой был уничтоженный объект. Напишем:

let jane = {
  name: "Jane"
};
let weakMap = new WeakMap([
  [jane, 1]
]);
jane = null;
console.log(weakMap.get(jane));

Получим undefined в console.log, так как значение jane равно null, и поэтому ссылка на исходный объект удаляется из памяти. 

Манипулируем WeakMaps

У WeakMaps меньше методов для управления записями, чем у Maps. У экземпляров WeakMap есть только методы get , set , delete и has.

get

Метод get используется для поиска значения по заданному ключу. Он принимает один аргумент — ключ, который используется для поиска значения. Его можно использовать так:

let jane = {
  name: "Jane"

};
let weakMap = new WeakMap([
  [jane, 1]
]);

console.log(weakMap.get(jane));

Мы получим 1 в качестве значения.

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

let jane = {
  name: "Jane"
};

let weakMap = new WeakMap([
  [jane, 1]
]);

console.log(weakMap.get({
  name: "Jane"
}));

Получим undefined, потому что должны передать jane , чтобы получить 1. У любого другого объекта с тем же содержимым нет той же ссылки в памяти. Вот почему мы не можем получить значение иначе как с исходным ключом. 

set

Метод set используется для добавления пары ключ-значение в WeakMap. Он принимает объект в качестве первого аргумента для ключа и любое значение для второго аргумента в качестве значения соответствующего ключа. Например, используем метод set:

let jane = {
  name: "Jane"
};

let john = {
  name: "john"
};

let weakMap = new WeakMap([
  [jane, 1]
]);

weakMap.set(john, 2);
console.log(weakMap.get(john));

delete

Метод delete принимает один аргумент — объект для key. Мы можем удалить его со ссылкой на объект: 

let jane = {
  name: "Jane"
};

let john = {
  name: "john"
};

let weakMap = new WeakMap([
  [jane, 1]
]);

weakMap.set(john, 2);
weakMap.delete(john)
console.log(weakMap.get(john));

Мы получим undefined в последней строке, если вызовем delete для john, поскольку таким образом удалим пару ключ-значение из WeakMap.

has

Метод has позволяет проверить существование значения для заданного ключа. Он принимает один аргумент — ключ, для которого мы ищем значение. 

Например:

let jane = {
  name: "Jane"
};

let weakMap = new WeakMap([
  [jane, 1]
]);

console.log(weakMap.has(jane));

Мы получим true из console.log, поскольку у нас есть запись с ключом jane.

И ещё раз, как и в методе get, мы должны использовать точную ссылку на объект в качестве ключа, чтобы получить значение true из метода has.

Следовательно, код ниже выдаст false:

let jane = {
  name: "Jane"
};

let weakMap = new WeakMap([
  [jane, 1]
]);

console.log(weakMap.has({
  name: "Jane"
}));

Мы получили false, потому что передали не точную ссылку на объект для [jane, 1]. Ключом является jane, а не другой объект, похожий на jane, ведь ссылка на него в памяти будет отличаться.

WeakMaps удобна для хранения данных, которые легко уничтожить, поскольку ключами являются точные ссылки на объекты. Также она удобна для хранения данных, которые сборщик мусора может собрать после того, как ссылки на них удаляются. Упрощение сборки мусора упрощает процесс освобождения ресурсов. 

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

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи John Au-Yeung: Storing Data Efficiently or Privately with JavaScript WeakMaps