Apa perbedaan antara ES6 Map dan WeakMap?

94

Melihat ini dan ini halaman MDN sepertinya satu-satunya perbedaan antara Maps dan WeakMaps adalah hilang "ukuran" properti untuk WeakMaps. Tapi apakah ini benar? Apa perbedaan diantara mereka?

Dmitrii Sorin
sumber
Efeknya ada pada GC. WeakMaps dapat mengumpulkan kuncinya.
John Dvorak
@JanDvorak tidak ada contoh yang ditunjukkan di MDN tentang hal itu. Seperti aWeakMap.get (key); // katakan, 2 ... (aksi GC) ... aWeakMap.get (key); // katakanlah, tidak ditentukan
Dmitrii Sorin
1
Teladan Anda tidak mungkin. keytidak dapat dikumpulkan, karena dirujuk oleh Anda.
John Dvorak
1
Keputusan desainnya adalah bahwa tindakan GC tidak terlihat di Javascript. Anda tidak dapat mengamati GC melakukan tugasnya.
John Dvorak
1
Lihat jawaban terkait ini untuk informasi lebih lanjut tentang masalah ini.
Benjamin Gruenbaum

Jawaban:

54

Dari halaman yang sama, bagian " Mengapa Peta Lemah ? " :

Pemrogram JavaScript yang berpengalaman akan melihat bahwa API ini dapat diimplementasikan dalam JavaScript dengan dua larik (satu untuk kunci, satu untuk nilai) yang digunakan bersama oleh 4 metode API. Implementasi seperti itu akan memiliki dua ketidaknyamanan utama. Yang pertama adalah pencarian O (n) (n adalah jumlah tombol di peta). Yang kedua adalah masalah kebocoran memori. Dengan peta yang ditulis secara manual, larik kunci akan menyimpan referensi ke objek utama, mencegahnya untuk dikumpulkan dari sampah. Dalam WeakMaps asli, referensi ke objek utama disimpan "dengan lemah" , yang berarti bahwa mereka tidak mencegah pengumpulan sampah jika tidak akan ada referensi lain ke objek tersebut.

Karena referensi lemah, kunci WeakMap tidak dapat dihitung (yaitu tidak ada metode yang memberi Anda daftar kunci). Jika ya, daftar tersebut akan bergantung pada status pengumpulan sampah, memperkenalkan non-determinisme.

[Dan itulah mengapa mereka juga tidak memiliki sizeproperti]

Jika Anda ingin memiliki daftar kunci, Anda harus menyimpannya sendiri. Ada juga proposal ECMAScript yang bertujuan untuk memperkenalkan set dan peta sederhana yang tidak akan menggunakan referensi yang lemah dan akan dapat dihitung.

- yang akan menjadi "normal" Maps . Tidak disebutkan di MDN, tetapi dalam proposal harmoni , mereka juga memiliki items, keysdan valuesmetode generator dan mengimplementasikan Iteratorantarmuka .

Bergi
sumber
jadi new Map().get(x)memiliki waktu pencarian yang hampir sama dengan membaca properti dari objek biasa?
Alexander Mills
1
@AlexanderMills Saya tidak melihat apa hubungannya dengan pertanyaan ini, tapi ini beberapa datanya . Secara umum, ya mereka mirip , dan Anda harus menggunakan yang sesuai .
Bergi
Jadi pemahaman saya adalah Map mempertahankan array internal untuk mempertahankan kuncinya karena array itu. Pengumpul sampah tidak bisa menahan referensi. Di WeekMap, ia tidak memiliki larik di mana kuncinya dipertahankan sehingga kunci tanpa referensi dapat dikumpulkan.
Mohan Ram
@MohanRam A WeakMapmasih memiliki larik (atau kumpulan entri lainnya), ini hanya memberi tahu pengumpul sampah bahwa itu adalah referensi yang lemah .
Bergi
Lalu mengapa iterasi untuk kunci WeekMap tidak didukung?
Mohan Ram
93

Keduanya berperilaku berbeda saat objek yang direferensikan oleh kunci / nilainya dihapus. Mari kita ambil contoh kode di bawah ini:

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 di atas dijalankan tidak ada cara kami dapat referensi {x: 12}dan {y: 12}lagi. Pengumpul sampah melanjutkan dan menghapus penunjuk b kunci dari "WeakMap" dan juga menghapus {y: 12}dari memori. Tetapi dalam kasus "Peta", pengumpul sampah tidak menghapus penunjuk dari "Peta" dan juga tidak menghapus {x: 12}dari memori.

Ringkasan: WeakMap memungkinkan pengumpul sampah melakukan tugasnya tetapi tidak dengan Map.

Referensi: http://qnimate.com/difference-between-map-and-weakmap-in-javascript/

kshirish
sumber
13
Mengapa tidak dihapus dari memori? Karena Anda masih bisa mereferensikannya! map.entries().next().value // [{x:12}, 1]
Bergi
4
Ini bukan fungsi yang dipanggil sendiri. Ini adalah ekspresi fungsi yang segera dipanggil. benalman.com/news/2010/11/…
Olson.dev
lalu apa perbedaan antara weakmap dan objek
Muhammad Umer
@MuhammadUmer: objek hanya dapat memiliki 'kunci' string, sedangkan WeakMaphanya dapat memiliki kunci non-primitif (tidak ada string atau angka atau Symbols sebagai kunci, hanya array, objek, peta lain, dll.).
Ahmed Fasih
1
@nnnnnn Ya itu perbedaannya, masih di Map tapi tidak diWeakMap
Alexander Derck
76

Mungkin penjelasan selanjutnya akan lebih jelas bagi seseorang.

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

Seperti yang Anda lihat, setelah menghapus k1kunci dari memori kita masih bisa mengaksesnya di dalam peta. Pada saat yang sama menghapus k2kunci WeakMap akan menghapusnya dariwm referensi.

Itulah mengapa WeakMap tidak memiliki metode enumerable seperti forEach, karena tidak ada yang namanya daftar kunci WeakMap, mereka hanya referensi ke objek lain.

Rax Wunter
sumber
10
di baris terakhir, tentu saja, wm.get (null) tidak akan ditentukan.
DaNeSh
8
Jawaban yang lebih baik daripada menyalin dan menempel dari situs mozilla, pujian.
Joel Hernandez
2
di forEach, (key, val)seharusnya benar-benar(val, key)
Miguel Mota
luar biasa bagaimana contoh yang tidak masuk akal mendapatkan begitu banyak
suara positif
34

Perbedaan lain (sumber: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):

Kunci dari WeakMaps adalah tipe Object saja. Tipe data primitif sebagai kunci tidak diperbolehkan (mis. Simbol tidak bisa menjadi kunci WeakMap).

String, angka, atau boolean juga tidak dapat digunakan sebagai WeakMapkunci. A Map dapat menggunakan nilai primitif untuk kunci.

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

m = new Map
m.set('a', 'b'); // Works
Trevor Dixon
sumber
6
Jika ada yang bertanya-tanya: Saya bisa membayangkan alasan di balik ini adalah: Anda tidak dapat menyimpan atau meneruskan referensi ke tipe primitif. Jadi kunci dalam WeakMap akan menjadi satu-satunya referensi. Dengan begitu pengumpulan sampah tidak mungkin dilakukan. Saya tidak tahu apakah referensi yang lemah tidak mungkin atau hanya tidak masuk akal. Tetapi bagaimanapun juga, kuncinya haruslah sesuatu yang dapat dirujuk dengan lemah.
Andreas Linnert
3

Dari Javascript.info

Peta - Jika kita menggunakan objek sebagai kunci dalam Peta biasa, maka saat Peta ada, objek itu juga ada. Ini menempati memori dan mungkin tidak dikumpulkan sampah.

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]

Mirip dengan itu, jika kita menggunakan objek sebagai kunci dalam Map biasa, maka selama Map ada, objek itu juga ada. Ini menempati memori dan mungkin tidak dikumpulkan sampah

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 - Sekarang, jika kita menggunakan objek sebagai kunci di dalamnya, dan tidak ada referensi lain ke objek itu - itu akan dihapus dari memori (dan dari peta) secara otomatis.

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

// john is removed from memory!
Avadhut Thorat
sumber
3

WeapMap di javascript tidak menyimpan kunci atau nilai apa pun, hanya memanipulasi nilai kunci menggunakan id unik dan menentukan properti ke objek kunci.

karena ia mendefinisikan properti menjadi key objectmenurut metode Object.definePropert(), kunci tidak boleh berjenis primitif .

dan juga karena WeapMap sebenarnya tidak berisi pasangan nilai kunci, kita tidak bisa mendapatkan properti length dari weakmap.

dan juga nilai yang dimanipulasi diberikan kembali ke objek kunci, pengumpul sampah dengan mudah dapat mengumpulkan kunci jika tidak digunakan.

Kode contoh untuk implementasi.

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

referensi implementasi

Ravi Sevta
sumber
1
Untuk lebih jelasnya, implementasi ini hanya berfungsi setengahnya. Itu tidak akan mengizinkan penggunaan objek yang sama sebagai kunci dalam beberapa peta yang lemah. Ini juga tidak berfungsi untuk benda beku. Dan tentu saja, itu membocorkan pemetaan kepada siapa saja yang memiliki referensi ke objek tersebut. Yang pertama bisa diperbaiki menggunakan simbol, tetapi dua yang terakhir tidak bisa.
Andreas Rossberg
@AndreasRossberg Dalam implementasi ini saya telah menambahkan hardcode id, tetapi ini harus unik dengan menggunakan sesuatu Math.random dan Date.now (), dll. Dan dengan menambahkan id dinamis ini, poin pertama dapat diselesaikan. Bisakah Anda memberi saya solusi untuk dua poin terakhir.
Ravi Sevta
Masalah pertama diselesaikan dengan lebih elegan dengan menggunakan simbol. Dua yang terakhir tidak dapat diselesaikan dalam JS, itulah sebabnya WeakMap harus menjadi primitif dalam bahasa tersebut.
Andreas Rossberg
1

WeakMap kunci harus berupa objek, bukan nilai primitif.

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

Mengapa????

Mari kita lihat contoh di bawah ini.

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

Jika kita menggunakan objek sebagai kunci reguler Map, maka sementara Mapada, objek itu juga ada. Ini menempati memori dan mungkin tidak dikumpulkan sampah.

WeakMapberbeda secara fundamental dalam aspek ini. Itu tidak mencegah pengumpulan sampah objek utama.

let user = { name: "User" };

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

user = null; // overwrite the reference

// 'user' is removed from memory!

jika kita menggunakan objek sebagai kunci di dalamnya, dan tidak ada referensi lain ke objek itu - itu akan dihapus dari memori (dan dari peta) secara otomatis.

WeakMap tidak mendukung iterasi dan metode kunci () , nilai () , entri () , jadi tidak ada cara untuk mendapatkan semua kunci atau nilai darinya.

WeakMap hanya memiliki metode berikut:

  • weakMap.get (key)
  • weakMap.set (kunci, nilai)
  • weakMap.delete (kunci)
  • weakMap.has (kunci)

Jelas seolah-olah sebuah objek telah kehilangan semua referensi lainnya (seperti 'pengguna' dalam kode di atas), maka objek tersebut akan dikumpulkan secara otomatis. Namun secara teknis itu tidak ditentukan secara pasti kapan pembersihan terjadi.

Mesin JavaScript memutuskan itu. Ini mungkin memilih untuk melakukan pembersihan memori segera atau menunggu dan melakukan pembersihan nanti ketika lebih banyak penghapusan terjadi. Jadi, secara teknis jumlah elemen saat ini dari a WeakMaptidak diketahui. Mesin mungkin telah membersihkannya atau tidak atau melakukannya sebagian. Oleh karena itu, metode yang mengakses semua kunci / nilai tidak didukung.

Catatan: - Area utama aplikasi WeakMap adalah penyimpanan data tambahan. Seperti meng-cache sebuah objek hingga objek tersebut mengumpulkan sampah.

Pravin Divraniya
sumber