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

Глядя на это и this страницы MDN кажется, что единственная разница между Maps и WeakMaps заключается в отсутствующем свойстве "размер" для WeakMaps. Но так ли это? В чем разница между ними?


person Dmitrii Sorin    schedule 24.03.2013    source источник
comment
Эффект на GC. WeakMaps могут собирать свои ключи.   -  person John Dvorak    schedule 25.03.2013
comment
@JanDvorak, в MDN нет примера, указывающего на это. Например, aWeakMap.get(key); // скажем, 2 ...(действие сборщика мусора)... aWeakMap.get(key); // скажем, не определено   -  person Dmitrii Sorin    schedule 25.03.2013
comment
Ваш пример невозможен. key невозможно собрать, так как на него ссылаетесь вы.   -  person John Dvorak    schedule 25.03.2013
comment
@JanDvorak Я только что указал, что в MDN нет примера действий GC в отношении WeakMaps и Maps. GC очистит память для удаленных ключей для них обоих, не так ли?   -  person Dmitrii Sorin    schedule 25.03.2013
comment
Дизайнерское решение заключается в том, что действия GC невидимы в Javascript. Вы не можете наблюдать, как GC делает свое дело.   -  person John Dvorak    schedule 25.03.2013
comment
Единственная разница заключается в использовании памяти браузером, если для map используются только weakMap методы.   -  person John Dvorak    schedule 25.03.2013
comment
Дополнительную информацию об этой проблеме см. в этом связанном ответе.   -  person Benjamin Gruenbaum    schedule 16.08.2015
comment
Я бы хотел, чтобы кто-нибудь объяснил, есть ли разница в производительности между Map и WeakMap. Это было задавался в другом месте, но этот вопрос был закрыт как дубликат этого. Однако ни один из текущих ответов на этот вопрос не касается производительности.   -  person qntm    schedule 19.02.2018


Ответы (7)


Из та же страница, раздел " Почему слабая карта?":

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

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

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

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

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

person Bergi    schedule 24.03.2013
comment
так что new Map().get(x) имеет примерно такое же время поиска, как чтение свойства из простого объекта? - person Alexander Mills; 05.05.2018
comment
@AlexanderMills Я не понимаю, какое это имеет отношение к вопросу, но вот некоторые данные. В общем, да, они похожи, и вы должны использовать соответствующий. - person Bergi; 05.05.2018
comment
Насколько я понимаю, Map поддерживает внутренний массив для сохранения своего ключа из-за этого массива. Сборщик мусора не может воздержаться от ссылки. В WeekMap нет массива, в котором хранятся ключи, поэтому ключ без ссылки может быть удален сборщиком мусора. - person Mohan Ram; 17.05.2018
comment
@MohanRam A WeakMap все еще имеет массив (или другую коллекцию) записей, он просто сообщает сборщику мусора, что это слабые ссылки. - person Bergi; 17.05.2018
comment
Тогда почему итерация для ключей WeekMap не поддерживается? - person Mohan Ram; 17.05.2018
comment
@MohanRam Проверьте второй абзац в первой цитате в ответе. - person Bergi; 17.05.2018
comment
@Bergi Я могу понять ссылку на неделю, но то, как компилятор обрабатывает ее как ссылку на неделю, я не получаю. Пожалуйста, поделитесь материалом, который поможет мне понять, как компилятор знает ссылку на неделю. Спасибо за вашу помощь - person Mohan Ram; 17.05.2018

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

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/

person kshirish    schedule 28.05.2015
comment
Почему не удаляется из памяти? Потому что вы все еще можете ссылаться на него! map.entries().next().value // [{x:12}, 1] - person Bergi; 29.05.2015
comment
Это не самовызываемая функция. Это немедленно вызываемое функциональное выражение. benalman.com/news/2010/11/ - person Olson.dev; 21.07.2015
comment
тогда какая разница между слабой картой и объектом - person Muhammad Umer; 12.08.2016
comment
@MuhammadUmer: объект может иметь только строковые «ключи», в то время как WeakMap может иметь только не примитивные ключи (без строк или чисел или Symbol в качестве ключей, только массивы, объекты, другие карты и т. д.). - person Ahmed Fasih; 28.08.2016
comment
мы больше не можем ссылаться на {x: 12} и {y: 12}. - Но {x: 12} все еще находится в Map, как вы продолжаете объяснять. - person nnnnnn; 05.05.2017
comment
@nnnnnn Да, в этом разница, это все еще в Map, но не в WeakMap - person Alexander Derck; 02.08.2017
comment
Этот ответ сбивает с толку из-за выбранных вами ключей. Когда вы думаете о ключе в js, вы традиционно думаете, что это должна быть строка. Лучше использовать такие ключи, как {} или function() {}. - person vaughan; 05.11.2017

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

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, это просто ссылки на другие объекты.

person Rax Wunter    schedule 23.01.2016
comment
в последней строке, конечно, wm.get(null) будет неопределенным. - person DaNeSh; 13.08.2016
comment
Лучший ответ, чем копирование и вставка с сайта Mozilla, спасибо. - person Joel Hernandez; 04.12.2016
comment
в forEach, (key, val) на самом деле должно быть (val, key) - person Miguel Mota; 20.01.2017
comment
невероятно, как пример, который не имеет смысла, получил столько голосов - person alfredopacino; 19.07.2020

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

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

Также нельзя использовать строку, число или логическое значение в качестве ключа WeakMap. 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
person Trevor Dixon    schedule 13.05.2015
comment
На случай, если кому-то интересно: я могу представить, что причина этого такова: вы не можете хранить или передавать ссылки на примитивные типы. Таким образом, ключ в WeakMap будет его единственной ссылкой. Таким образом, сбор мусора был бы невозможен. Я не знаю, невозможны ли слабые ссылки или просто не имеют смысла. Но в любом случае ключ должен быть чем-то, на что можно слабо ссылаться. - person Andreas Linnert; 08.01.2016

Из 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!
person Avadhut Thorat    schedule 02.06.2020

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

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

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

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

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

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;
})();

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

person Ravi Sevta    schedule 27.05.2018
comment
Чтобы было ясно, эта реализация работает только наполовину. Это не позволит использовать один и тот же объект в качестве ключа на нескольких слабых картах. Это также не работает для замороженных объектов. И, конечно же, он передает сопоставление всем, у кого есть ссылка на объект. Первое можно исправить с помощью символов, но не два последних. - person Andreas Rossberg; 12.07.2020
comment
@AndreasRossberg В этой реализации я добавил жестко запрограммированный id, но он должен быть уникальным, используя что-то Math.random и Date.now () и т. д. И добавив этот динамический идентификатор, можно решить первую точку. Не могли бы вы дать мне решение для последних двух пунктов. - person Ravi Sevta; 13.07.2020
comment
Первая проблема решается более элегантно с помощью символов. Последние два не могут быть решены в JS, поэтому WeakMap должен быть примитивом в языке. - person Andreas Rossberg; 13.07.2020

Ключи 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 — дополнительное хранилище данных. Например, кэширование объекта до тех пор, пока этот объект не будет собран мусором.

person Pravin Divraniya    schedule 22.08.2019