В чем разница между картой ES6 и WeakMap?


94

Глядя на эту и эту страницы MDN, кажется, что единственная разница между Картами и WeakMaps - это отсутствие свойства «размер» для WeakMaps. Но так ли это? В чем разница между ними?


Эффект есть на GC. WeakMaps может собирать свои ключи.
John Dvorak

@JanDvorak, в MDN нет примера. Как aWeakMap.get (ключ); // скажем, 2 ... (действие сборщика мусора) ... aWeakMap.get (key); // скажем, undefined
Дмитрий Сорин

1
Ваш пример невозможен. keyне могут быть собраны, потому что на него ссылаются вы.
John Dvorak

1
Дизайнерское решение состоит в том, что действия GC невидимы в Javascript. Вы не можете наблюдать, как GC делает свое дело.
John Dvorak

1
См. Соответствующий ответ для получения дополнительной информации об этой проблеме.
Бенджамин Грюнбаум,

Ответы:


54

С той же страницы, раздел « Почему слабая карта? » :

Опытный программист JavaScript заметит, что этот API может быть реализован в JavaScript с двумя массивами (один для ключей, один для значений), общих для 4 методов API. Такая реализация имела бы два основных неудобства. Первый - это поиск за O (n) (n - количество ключей на карте). Второй - проблема с утечкой памяти. С помощью карт, написанных вручную, массив ключей будет содержать ссылки на ключевые объекты, предотвращая их сборку мусора. В собственном WeakMaps ссылки на ключевые объекты хранятся «слабо» , что означает, что они не предотвращают сборку мусора в случае, если не будет другой ссылки на объект.

Из-за того, что ссылки слабые, ключи WeakMap нельзя перечислить (т. Е. Нет метода, дающего вам список ключей). Если бы это было так, список зависел бы от состояния сборки мусора, что привело бы к недетерминированности.

[И поэтому у них тоже нет sizeсобственности]

Если вы хотите иметь список ключей, вам следует вести его самостоятельно. Существует также предложение ECMAScript, направленное на введение простых наборов и карт, которые не будут использовать слабые ссылки и будут перечисляемыми.

- которые были бы «нормальными» Maps . Не упоминается в MDN, но в предложении гармонии , те также items, keysи valuesметоды генератора и реализовать Iteratorинтерфейс .


так new Map().get(x)что время поиска примерно такое же, как и при чтении свойства из простого объекта?
Александр Миллс

1
@AlexanderMills Я не понимаю, какое это имеет отношение к вопросу, но вот некоторые данные . В общем, да, они похожи , и вы должны использовать подходящий .
Берги

Итак, я понимаю, что Map поддерживает внутренний массив для сохранения своего ключа из-за этого массива. Сборщик мусора не может воздержаться от ссылки. В WeekMap у него нет массива, в котором хранятся ключи, поэтому ключ без ссылки может быть собран сборщиком мусора.
Мохан Рам

@MohanRam A WeakMapвсе еще имеет массив (или другую коллекцию) записей, он просто сообщает сборщику мусора, что это слабые ссылки .
Берги

Тогда почему не поддерживаются итерации для ключей WeekMap?
Мохан Рам

93

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

var map = new Map();
var weakmap = new WeakMap();

(function(){
    var a = {x: 12};
    var b = {y: 12};

    map.set(a, 1);
    weakmap.set(b, 2);
})()

Вышеупомянутый IIFE выполняется, мы не можем ссылаться {x: 12}и {y: 12}больше. Сборщик мусора удаляет указатель ключа b из «WeakMap», а также удаляет его {y: 12}из памяти. Но в случае «Карта» сборщик мусора не удаляет указатель с «Карты», а также не удаляет {x: 12}из памяти.

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

Ссылки: http://qnimate.com/difference-between-map-and-weakmap-in-javascript/


13
Почему не удаляется из памяти? Потому что вы все еще можете ссылаться на него! map.entries().next().value // [{x:12}, 1]
Берги

4
Это не вызываемая пользователем функция. Это выражение функции, вызываемое немедленно. benalman.com/news/2010/11/…
Olson.dev

Тогда какая разница между weakmap и object
Мухаммад Умер

@MuhammadUmer: объект может иметь только строковые «ключи», тогда как WeakMapможет иметь только непримитивные ключи (без строк, чисел или Symbols в качестве ключей, только массивы, объекты, другие карты и т. Д.).
Ахмед Фасих

1
@nnnnnn Да вот в чем разница, она все еще в, Map но не вWeakMap
Александр Дерк

76

Возможно, следующее объяснение будет для кого-то более понятным.

var k1 = {a: 1};
var k2 = {b: 2};

var map = new Map();
var wm = new WeakMap();

map.set(k1, 'k1');
wm.set(k2, 'k2');

k1 = null;
map.forEach(function (val, key) {
    console.log(key, val); // k1 {a: 1}
});

k2 = null;
wm.get(k2); // undefined

Как видите, после удаления k1ключа из памяти мы все еще можем получить к нему доступ внутри карты. В то же время удаление k2ключа WeakMap удаляет его изwm по ссылке.

Вот почему WeakMap не имеет перечислимых методов, таких как forEach, потому что не существует такой вещи, как список ключей WeakMap, это просто ссылки на другие объекты.


10
в последней строке, конечно, wm.get (null) будет неопределенным.
DaNeSh,

8
Лучший ответ, чем копирование и вставка с сайта mozilla, престиж.
Joel Hernandez

2
в forEach, (key, val)должно быть на самом деле(val, key)
Мигель Мота

невероятно, как бессмысленный пример получил столько голосов
Альфредопачино

34

Еще одно отличие (источник: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):

Ключи WeakMaps относятся только к типу Object. Примитивные типы данных в качестве ключей не допускаются (например, символ не может быть ключом WeakMap).

Также нельзя использовать строку, число или логическое значение в качестве WeakMapключа. A Map может использовать примитивные значения для ключей.

w = new WeakMap;
w.set('a', 'b'); // Uncaught TypeError: Invalid value used as weak map key

m = new Map
m.set('a', 'b'); // Works

6
В случае, если кто-то задается вопросом: я могу представить причину этого: вы не можете сохранять или передавать ссылки на примитивные типы. Таким образом, ключ в WeakMap будет его единственной ссылкой. Таким образом, сборка мусора была бы невозможна. Я не знаю, невозможны ли слабые ссылки или они просто не имеют смысла. Но в любом случае ключ должен быть чем-то, на что можно ссылаться слабо.
Андреас Линнерт

3

Из Javascript.info

Карта - если мы используем объект в качестве ключа в обычной карте, то пока существует карта, этот объект также существует. Он занимает память и не подлежит сборке мусора.

let john = { name: "John" };
let array = [ john ];
john = null; // overwrite the reference

// john is stored inside the array, so it won't be garbage-collected
// we can get it as array[0]

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

let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // overwrite the reference

// john is stored inside the map,
// we can get it by using map.keys()

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

let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // overwrite the reference

// john is removed from memory!

3

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

поскольку он определяет свойство key objectпо методу Object.definePropert(), ключ не должен быть примитивного типа .

а также из-за того, что WeapMap фактически не содержит пар ключ-значение, мы не можем получить свойство length для weakmap.

а также управляемое значение присваивается обратно ключевому объекту, сборщик мусора легко может собрать ключ, если он не используется.

Пример кода для реализации.

if(typeof WeapMap != undefined){
return;
} 
(function(){
   var WeapMap = function(){
      this.__id = '__weakmap__';
   }
        
   weakmap.set = function(key,value){
       var pVal = key[this.__id];
        if(pVal && pVal[0] == key){
           pVal[1]=value;
       }else{
          Object.defineProperty(key, this.__id, {value:[key,value]});
          return this;
        }
   }

window.WeakMap = WeakMap;
})();

ссылка на реализацию


1
Чтобы было понятно, эта реализация работает только наполовину. Это не позволит использовать один и тот же объект в качестве ключа на нескольких слабых картах. Это также не работает для замороженных объектов. И, конечно же, он передает отображение любому, у кого есть ссылка на объект. Первый можно исправить с помощью символов, но не два последних.
Андреас Россберг,

@AndreasRossberg В этой реализации я добавил жестко idзапрограммированный код, но он должен быть уникальным за счет использования чего-то Math.random и Date.now () и т. Д. И добавив этот динамический идентификатор, можно решить первую проблему. Не могли бы вы дать мне решение по последним двум пунктам.
Рави Севта,

Первая проблема решается более элегантно с помощью символов. Последние два не могут быть решены в JS, поэтому WeakMap должен быть примитивом в языке.
Андреас Россберг

1

WeakMap ключи должны быть объектами, а не примитивными значениями.

let weakMap = new WeakMap();

let obj = {};

weakMap.set(obj, "ok"); // works fine (object key)

// can't use a string as the key
weakMap.set("test", "Not ok"); // Error, because "test" is not an object

Зачем????

Давайте посмотрим на пример ниже.

let user = { name: "User" };

let map = new Map();
map.set(user, "...");

user = null; // overwrite the reference

// 'user' is stored inside the map,
// We can get it by using map.keys()

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

WeakMapпринципиально отличается в этом аспекте. Это не предотвращает сборку мусора ключевых объектов.

let user = { name: "User" };

let weakMap = new WeakMap();
weakMap.set(user, "...");

user = null; // overwrite the reference

// 'user' is removed from memory!

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

WeakMap не поддерживает итерацию и методы keys () , values ​​() , entries () , поэтому получить из него все ключи или значения невозможно.

WeakMap имеет только следующие методы:

  • weakMap.get (ключ)
  • weakMap.set (ключ, значение)
  • weakMap.delete (ключ)
  • weakMap.has (ключ)

Это очевидно, как если бы объект потерял все другие ссылки (например, «пользователь» в приведенном выше коде), он должен быть автоматически собран в мусор. Но технически точно не указано, когда произойдет очистка.

Это решает движок JavaScript. Он может выбрать немедленную очистку памяти или подождать и выполнить очистку позже, когда произойдет еще несколько удалений. Итак, технически текущее количество элементов WeakMapнеизвестно. Двигатель мог очистить его или нет, или сделал это частично. По этой причине методы, которые обращаются ко всем ключам и значениям, не поддерживаются.

Примечание: - Основная область применения WeakMap - дополнительное хранилище данных. Это похоже на кеширование объекта, пока этот объект не будет собран сборщиком мусора.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.