Sự khác biệt giữa ES6 Map và WeakMap là gì?


93

Nhìn vào trang nàytrang MDN này , có vẻ như sự khác biệt duy nhất giữa Maps và WeakMaps là thiếu thuộc tính "kích thước" cho WeakMaps. Nhưng điều này có đúng không? Sự khác biệt giữa chúng là gì?


Hiệu quả là trên GC. WeakMaps có thể thu thập khóa của chúng.
John Dvorak

@JanDvorak không có ví dụ nào được chỉ ra trên MDN về nó. Giống như aWeakMap.get (key); // say, 2 ... (GC action) ... aWeakMap.get (key); // nói, không xác định
Dmitrii Sorin

1
Ví dụ của bạn là không thể. keykhông thể được thu thập, bởi vì nó được tham chiếu bởi bạn.
John Dvorak

1
Quyết định thiết kế là các hành động GC là ẩn trong Javascript. Bạn không thể quan sát GC làm việc của mình.
John Dvorak

1
Xem câu trả lời liên quan này để biết thêm thông tin về vấn đề này.
Benjamin Gruenbaum

Câu trả lời:


53

Từ cùng một trang, phần " Tại sao Bản đồ yếu ? " :

Lập trình viên JavaScript có kinh nghiệm sẽ nhận thấy rằng API này có thể được triển khai bằng JavaScript với hai mảng (một cho khóa, một cho giá trị) được chia sẻ bởi 4 phương thức API. Việc triển khai như vậy sẽ có hai điểm bất tiện chính. Đầu tiên là tìm kiếm O (n) (n là số lượng khóa trong bản đồ). Vấn đề thứ hai là vấn đề rò rỉ bộ nhớ. Với bản đồ được viết thủ công, mảng khóa sẽ giữ các tham chiếu đến các đối tượng chính, ngăn chúng bị thu thập rác. Trong WeakMaps bản địa, các tham chiếu đến các đối tượng chính được giữ "yếu" , có nghĩa là chúng không ngăn chặn việc thu gom rác trong trường hợp không có tham chiếu nào khác đến đối tượng.

Vì các tham chiếu yếu, các khóa WeakMap không thể liệt kê được (tức là không có phương pháp nào cung cấp cho bạn danh sách các khóa). Nếu có, danh sách sẽ phụ thuộc vào tình trạng thu gom rác, đưa ra thuyết không xác định.

[Và đó là lý do tại sao họ cũng không có sizetài sản]

Nếu bạn muốn có một danh sách các chìa khóa, bạn nên tự bảo quản nó. Ngoài ra còn có một đề xuất ECMAScript nhằm giới thiệu các bộ và bản đồ đơn giản không sử dụng các tham chiếu yếu và có thể liệt kê được.

- đó sẽ là "bình thường" Maps . Không được đề cập ở MDN, nhưng trong đề xuất hài hòa , những người đó cũng có items, keysvaluescác phương pháp trình tạo và triển khai Iteratorgiao diện .


vậy new Map().get(x)có cùng thời gian tra cứu như đọc một thuộc tính từ một đối tượng đơn giản không?
Alexander Mills

1
@AlexanderMills Tôi không thấy điều này có liên quan gì đến câu hỏi, nhưng đây là một số dữ liệu . Nói chung, , chúng tương tự nhau , và bạn nên sử dụng cái thích hợp .
Bergi

Vì vậy, hiểu biết của tôi là Bản đồ duy trì một mảng bên trong để duy trì khóa của nó vì mảng đó. Trình thu gom rác không thể lọc tham chiếu. Trong WeekMap, nó không có một mảng mà các khóa được duy trì để khóa không có tham chiếu có thể được thu thập rác.
Mohan Ram

@MohanRam A WeakMapvẫn có một mảng (hoặc bộ sưu tập khác) các mục nhập, nó chỉ cho bộ thu gom rác biết rằng đó là các tham chiếu yếu .
Bergi

Sau đó, tại sao lặp lại cho các khóa WeekMap không được hỗ trợ?
Mohan Ram

92

Cả hai đều hoạt động khác nhau khi một đối tượng được tham chiếu bởi các khóa / giá trị của chúng bị xóa. Hãy lấy mã ví dụ dưới đây:

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 ở trên được thực thi không có cách nào chúng ta có thể tham khảo {x: 12}{y: 12}nữa. Bộ thu gom rác sẽ tiếp tục và xóa con trỏ khóa b khỏi “Bản đồ yếu” và cũng xóa {y: 12}khỏi bộ nhớ. Nhưng trong trường hợp “Bản đồ”, trình thu gom rác không xóa một con trỏ khỏi “Bản đồ” và cũng không xóa {x: 12}khỏi bộ nhớ.

Tóm tắt: WeakMap cho phép bộ thu gom rác thực hiện nhiệm vụ của nó nhưng không cho phép Bản đồ.

Tài liệu tham khảo: http://qnimate.com/difference-between-map-and-weakmap-in-javascript/


12
Tại sao nó không bị xóa khỏi bộ nhớ? Vì bạn vẫn có thể tham khảo nó! map.entries().next().value // [{x:12}, 1]
Bergi

4
Nó không phải là một hàm tự gọi. Đó là một biểu thức hàm được gọi ngay lập tức. benalman.com/news/2010/11/…
Olson.dev

sau đó một sự khác biệt giữa weakmap và đối tượng là những gì
Muhammad Umer

@MuhammadUmer: đối tượng chỉ có thể có chuỗi 'khóa', trong khi WeakMapchỉ có thể có các khóa không phải nguyên thủy (không có chuỗi hoặc số hoặc Symbols làm khóa, chỉ có mảng, đối tượng, bản đồ khác, v.v.).
Ahmed Fasih

1
@nnnnnn Có đó là sự khác biệt, nó vẫn còn trong Map nhưng không phải trongWeakMap
Alexander Derck

75

Có thể lời giải thích tiếp theo sẽ rõ ràng hơn cho ai đó.

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

Như bạn thấy, sau khi xóa k1khóa khỏi bộ nhớ, chúng ta vẫn có thể truy cập nó bên trong bản đồ. Đồng thời xóa k2khóa của WeakMap cũng xóa nó khỏi wmbằng cách tham khảo.

Đó là lý do tại sao WeakMap không liệt kê các phương thức như forEach, vì không có cái gọi là danh sách các khóa WeakMap, chúng chỉ là tham chiếu đến các đối tượng khác.


10
ở dòng cuối cùng, tất nhiên, wm.get (null) sẽ không được xác định.
DaNeSh

8
Câu trả lời tốt hơn là sao chép và dán từ trang web mozilla, kudos.
Joel Hernandez

2
trong forEach, (key, val)nên thực sự là(val, key)
Miguel Mota

không thể tin được làm thế nào mà một ví dụ vô nghĩa lại nhận được nhiều
ủng hộ

34

Một sự khác biệt khác (nguồn: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):

Khóa của Bản đồ yếu chỉ thuộc loại Đối tượng. Không cho phép các kiểu dữ liệu nguyên thủy làm khóa (ví dụ: Biểu tượng không được là khóa Bản đồ yếu).

Cũng không thể sử dụng một chuỗi, số hoặc boolean làm WeakMapkhóa. A Map có thể sử dụng các giá trị nguyên thủy cho các khóa.

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
Trong trường hợp có ai thắc mắc: Tôi có thể tưởng tượng lý do đằng sau điều này là: bạn không thể giữ hoặc chuyển các tham chiếu đến các kiểu nguyên thủy. Vì vậy, khóa trong WeakMap sẽ là tài liệu tham khảo duy nhất của nó. Bằng cách đó, việc thu gom rác sẽ không thể thực hiện được. Tôi không biết liệu các tham chiếu yếu là không thể hay chỉ là không có ý nghĩa. Nhưng dù theo cách nào thì khóa cũng cần phải là thứ có thể được tham chiếu yếu.
Andreas Linnert

3

Từ Javascript.info

Bản đồ - Nếu chúng ta sử dụng một đối tượng làm khóa trong Bản đồ thông thường, thì trong khi Bản đồ tồn tại, đối tượng đó cũng tồn tại. Nó chiếm bộ nhớ và có thể không được thu thập rác.

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]

Tương tự như vậy, nếu chúng ta sử dụng một đối tượng làm khóa trong Bản đồ thông thường, thì trong khi Bản đồ tồn tại, đối tượng đó cũng tồn tại. Nó chiếm bộ nhớ và có thể không được thu gom rác

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 - Bây giờ, nếu chúng ta sử dụng một đối tượng làm khóa trong đó và không có tham chiếu nào khác đến đối tượng đó - nó sẽ tự động bị xóa khỏi bộ nhớ (và khỏi bản đồ).

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

// john is removed from memory!

3

WeapMap trong javascript không chứa bất kỳ khóa hoặc giá trị nào, nó chỉ thao tác giá trị khóa bằng cách sử dụng một id duy nhất và xác định thuộc tính cho đối tượng khóa.

bởi vì nó xác định thuộc tính key objecttheo phương thức Object.definePropert(), khóa không được là kiểu nguyên thủy .

và cũng bởi vì WeapMap không chứa các cặp giá trị khóa thực sự, chúng ta không thể lấy thuộc tính độ dài của bản đồ yếu.

và giá trị thao tác cũng được gán lại cho đối tượng khóa, người thu gom rác có thể dễ dàng thu thập khóa nếu nó không sử dụng.

Mã mẫu để thực hiện.

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

tham chiếu việc thực hiện


1
Để rõ ràng, việc triển khai này chỉ hoạt động một nửa. Nó sẽ không cho phép sử dụng cùng một đối tượng làm chìa khóa trong nhiều bản đồ yếu. Nó cũng không hoạt động đối với các đối tượng đông lạnh. Và tất nhiên, nó làm rò rỉ ánh xạ cho bất kỳ ai có tham chiếu đến đối tượng. Đầu tiên có thể được sửa bằng cách sử dụng các ký hiệu, nhưng không phải hai ký hiệu sau.
Andreas Rossberg

@AndreasRossberg Trong triển khai này, tôi đã thêm mã cứng id, nhưng điều này sẽ là duy nhất bằng cách sử dụng một cái gì đó Math.random và Date.now (), v.v. Và bằng cách thêm id động này, điểm đầu tiên có thể được giải quyết. Bạn có thể vui lòng cung cấp cho tôi một giải pháp cho hai điểm cuối cùng.
Ravi Sevta

Vấn đề đầu tiên được giải quyết một cách thanh lịch hơn bằng cách sử dụng các biểu tượng. Hai phần sau không thể được giải quyết trong JS, đó là lý do tại sao WeakMap phải là một ngôn ngữ nguyên thủy.
Andreas Rossberg

1

WeakMap khóa phải là đối tượng, không phải giá trị nguyên thủy.

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

Tại sao????

Hãy xem ví dụ dưới đây.

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

Nếu chúng ta sử dụng một đối tượng làm khóa trong Map , thì trong khi Maptồn tại, đối tượng đó cũng tồn tại. Nó chiếm bộ nhớ và có thể không được thu thập rác.

WeakMaplà khác nhau cơ bản ở khía cạnh này. Nó không ngăn chặn việc thu gom rác của các đối tượng chính.

let user = { name: "User" };

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

user = null; // overwrite the reference

// 'user' is removed from memory!

nếu chúng ta sử dụng một đối tượng làm khóa trong đó và không có tham chiếu nào khác đến đối tượng đó - nó sẽ tự động bị xóa khỏi bộ nhớ (và khỏi bản đồ).

WeakMap không hỗ trợ lặp lại và các phương thức khóa () , giá trị () , mục () , vì vậy không có cách nào để lấy tất cả các khóa hoặc giá trị từ nó.

WeakMap chỉ có các phương thức sau:

  • yếuMap.get (phím)
  • yếuMap.set (khóa, giá trị)
  • yếuMap.delete (phím)
  • yếuMap.has (phím)

Điều đó hiển nhiên như thể một đối tượng đã mất tất cả các tham chiếu khác (như 'người dùng' trong đoạn mã trên), thì nó sẽ được tự động thu thập. Nhưng về mặt kỹ thuật, nó không được chỉ định chính xác khi nào việc dọn dẹp diễn ra.

Công cụ JavaScript quyết định điều đó. Nó có thể chọn thực hiện dọn dẹp bộ nhớ ngay lập tức hoặc đợi và thực hiện dọn dẹp sau khi có nhiều lần xóa hơn. Vì vậy, về mặt kỹ thuật, số phần tử hiện tại của a WeakMapchưa được biết. Động cơ có thể đã làm sạch nó hoặc không hoặc đã làm nó một phần. Vì lý do đó, các phương pháp truy cập tất cả các khóa / giá trị không được hỗ trợ.

Lưu ý: - Khu vực ứng dụng chính của WeakMap là nơi lưu trữ dữ liệu bổ sung. Giống như bộ nhớ đệm một đối tượng cho đến khi đối tượng đó được thu gom rác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.