Яка різниця між ES6 Map та WeakMap?


93

Переглядаючи цю та ці сторінки MDN, здається, єдиною відмінністю між Картами та WeakMaps є відсутність властивості "розміру" для WeakMaps. Але чи це правда? Яка різниця між ними?


Вплив на ГХ. WeakMaps може отримати ключі.
Джон Дворжак,

@JanDvorak про це немає жодного прикладу, вказаного на MDN. Як aWeakMap.get (ключ); // скажімо, 2 ... (дія GC) ... aWeakMap.get (ключ); // скажімо, невизначено
Дмитро Сорін,

1
Ваш приклад неможливий. keyнеможливо зібрати, оскільки на нього посилаються.
Джон Дворжак,

1
Рішенням проекту є те, що дії GC невидимі в Javascript. Ви не можете спостерігати, як GC робить свою справу.
Джон Дворжак,

1
Для отримання додаткової інформації про цю проблему див. Відповідну відповідь .
Бенджамін Груенбаум,

Відповіді:


53

З тієї самої сторінки, розділу " Чому слабка карта? " :

Досвідчений програміст 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 не підтримується?
Мохан Рам,

92

Вони обидва поводяться по-різному, коли об'єкт, на який посилаються їх ключі / значення, видаляється. Візьмемо наведений нижче приклад коду:

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}з пам'яті. Але у випадку “Map”, збирач сміття не видаляє вказівник з “Map”, а також не видаляє {x: 12}з пам'яті.

Короткий зміст: WeakMap дозволяє збиральнику сміття виконувати своє завдання, але не Map.

Посилання: http://qnimate.com/difference-between-map-and-weakmap-in-javascript/


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

4
Це не самовикликана функція. Це негайно викликаний вираз функції. benalman.com/news/2010/11/…
Olson.dev

тоді яка різниця між слабкою картою та об’єктом
Мухаммад Умер

@MuhammadUmer: об'єкт може мати лише рядкові `` ключі '', тоді як WeakMapможе мати лише непримітивні ключі (без рядків чи цифр або Symbols як ключі, лише масиви, об'єкти, інші карти тощо).
Ахмед Фасіх,

1
@nnnnnn Так, це різниця, вона все ще в, Map але не вWeakMap
Олександр Дерк

75

Можливо, наступне пояснення буде для когось більш зрозумілим.

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, слава.
Джоель Ернандес,

2
у forEach, (key, val)має бути насправді(val, key)
Мігель Мота

неймовірно, як приклад, який не має сенсу, отримує стільки голосів
альфредопакіно,

34

Ще одна відмінність (джерело: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):

Клавіші WeakMaps мають лише тип "Об'єкт". Примітивні типи даних як ключі заборонені (наприклад, символ не може бути ключем 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 не містить ключів або значень, він просто маніпулює значенням ключа за допомогою унікального ідентифікатора та визначає властивість для об'єкта ключа.

оскільки він визначає властивість to 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;
})();

посилання на реалізацію


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 має лише такі методи:

  • слабМап.гет (ключ)
  • слабМап.сет (ключ, значення)
  • слабоМапа.видалити (ключ)
  • слабша карта. має (ключ)

Це очевидно, ніби об'єкт втратив усі інші посилання (наприклад, "користувач" у наведеному вище коді), тоді його слід збирати сміття автоматично. Але технічно точно не вказано, коли відбувається очищення.

Двигун JavaScript вирішує це. Він може вирішити негайно виконати очищення пам'яті або зачекати та провести очищення пізніше, коли відбудеться більше видалень. Отже, технічно поточна кількість елементів a WeakMapневідома. Мотор, можливо, чистив його чи ні, або робив це частково. З цієї причини методи, які отримують доступ до всіх ключів / значень, не підтримуються.

Примітка: - Основною областю застосування WeakMap є додаткове зберігання даних. Як кешування об’єкта, доки об’єкт не отримає зібране сміття.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.