Переглядаючи цю та ці сторінки MDN, здається, єдиною відмінністю між Картами та WeakMaps є відсутність властивості "розміру" для WeakMaps. Але чи це правда? Яка різниця між ними?
key
неможливо зібрати, оскільки на нього посилаються.
key
неможливо зібрати, оскільки на нього посилаються.
Відповіді:
З тієї самої сторінки, розділу " Чому слабка карта? " :
Досвідчений програміст JavaScript помітить, що цей API може бути реалізований у JavaScript із двома масивами (один для ключів, один для значень), спільними для 4 методів API. Така реалізація мала б дві основні незручності. Перший - це пошук O (n) (n - кількість ключів на карті). Другий - це проблема витоку пам’яті. За допомогою карт, написаних вручну, масив ключів зберігатиме посилання на ключові об’єкти, не даючи їм збирати сміття. У рідних WeakMaps посилання на ключові об'єкти містяться "слабо" , що означає, що вони не перешкоджають збору сміття, якщо іншого об'єкта не буде.
Через слабкість посилань ключів WeakMap не можна перерахувати (тобто не існує методу, що дає вам список ключів). Якби вони були, список залежав би від стану вивезення сміття, вводячи недетермінованість.
[І тому вони також не мають size
власності]
Якщо ви хочете мати список ключів, вам слід його вести самостійно. Існує також пропозиція ECMAScript, спрямована на введення простих наборів та карт, які не використовуватимуть слабких посилань і будуть перелічені.
- які були б «нормальними» Map
s . Не згадується в MDN, але в пропозиції гармонії вони також мають items
, keys
а також values
методи генератора та реалізацію Iterator
інтерфейсу .
new Map().get(x)
, приблизно такий же час пошуку, як читання властивості з простого об’єкта?
WeakMap
все ще має масив (або іншу колекцію) записів, він просто повідомляє збирачу сміття, що це слабкі посилання .
Вони обидва поводяться по-різному, коли об'єкт, на який посилаються їх ключі / значення, видаляється. Візьмемо наведений нижче приклад коду:
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/
map.entries().next().value // [{x:12}, 1]
WeakMap
може мати лише непримітивні ключі (без рядків чи цифр або Symbol
s як ключі, лише масиви, об'єкти, інші карти тощо).
Map
але не вWeakMap
Можливо, наступне пояснення буде для когось більш зрозумілим.
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, це просто посилання на інші об'єкти.
forEach
, (key, val)
має бути насправді(val, key)
Ще одна відмінність (джерело: 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
З 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!
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;
})();
посилання на реалізацію
id
, але це повинно бути унікальним, використовуючи щось Math.random і Date.now () тощо. І додавши цей динамічний ідентифікатор, можна вирішити перший момент. Не могли б ви надати мені рішення щодо останніх двох пунктів.
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 є додаткове зберігання даних. Як кешування об’єкта, доки об’єкт не отримає зібране сміття.