Công dụng thực tế của ES6 WeakMap là gì?


397

Việc sử dụng thực tế của WeakMapcấu trúc dữ liệu được giới thiệu trong ECMAScript 6 là gì?

Vì khóa của bản đồ yếu tạo tham chiếu mạnh đến giá trị tương ứng của nó, đảm bảo rằng giá trị được chèn vào bản đồ yếu sẽ không bao giờ biến mất miễn là khóa của nó vẫn còn sống, không thể sử dụng cho bảng ghi nhớ, bộ nhớ cache hoặc bất cứ thứ gì khác mà bạn thường sử dụng các tham chiếu yếu, bản đồ có giá trị yếu, v.v.

Dường như với tôi rằng:

weakmap.set(key, value);

... chỉ là một cách nói vòng vo để nói điều này:

key.value = value;

Những trường hợp sử dụng cụ thể tôi đang thiếu?




35
Trường hợp sử dụng trong thế giới thực: Lưu trữ dữ liệu tùy chỉnh cho các nút DOM.
Felix Kling

Tất cả các trường hợp sử dụng mà bạn đề cập cho các tài liệu tham khảo yếu cũng rất quan trọng. Chúng chỉ khó hơn rất nhiều để thêm vào ngôn ngữ kể từ khi chúng giới thiệu chủ nghĩa không điều kiện. Mark Miller và những người khác đã thực hiện rất nhiều công việc về các tài liệu tham khảo yếu và tôi nghĩ cuối cùng họ cũng sẽ đến. Cuối cùng
Benjamin Gruenbaum

2
WeakMaps có thể được sử dụng để phát hiện rò rỉ bộ nhớ: stevehanov.ca/blog/?id=148
theWebalyst

Câu trả lời:


513

Về cơ bản

WeakMaps cung cấp một cách để mở rộng các đối tượng từ bên ngoài mà không can thiệp vào việc thu gom rác. Bất cứ khi nào bạn muốn mở rộng một đối tượng nhưng không thể vì nó được niêm phong - hoặc từ một nguồn bên ngoài - WeakMap có thể được áp dụng.

WeakMap là một bản đồ (từ điển) trong đó các khóa yếu - nghĩa là, nếu tất cả các tham chiếu đến khóa bị mất và không còn tham chiếu đến giá trị - giá trị có thể được thu gom rác. Chúng ta hãy thể hiện điều này trước qua các ví dụ, sau đó giải thích một chút và cuối cùng kết thúc với việc sử dụng thực sự.

Giả sử tôi đang sử dụng API cung cấp cho tôi một đối tượng nhất định:

var obj = getObjectFromLibrary();

Bây giờ, tôi có một phương thức sử dụng đối tượng:

function useObj(obj){
   doSomethingWith(obj);
}

Tôi muốn theo dõi số lần phương thức được gọi với một đối tượng nhất định và báo cáo nếu nó xảy ra nhiều hơn N lần. Người ta thường nghĩ sẽ sử dụng Bản đồ:

var map = new Map(); // maps can have object keys
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

Điều này hoạt động, nhưng nó bị rò rỉ bộ nhớ - bây giờ chúng tôi theo dõi mọi đối tượng thư viện duy nhất được chuyển đến chức năng giữ cho các đối tượng thư viện không bị thu gom rác. Thay vào đó - chúng ta có thể sử dụng WeakMap:

var map = new WeakMap(); // create a weak map
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

Và rò rỉ bộ nhớ đã biến mất.

Trường hợp sử dụng

Một số trường hợp sử dụng có thể gây rò rỉ bộ nhớ và được bật bởi WeakMaps bao gồm:

  • Giữ dữ liệu riêng tư về một đối tượng cụ thể và chỉ cấp quyền truy cập cho những người có tham chiếu đến Bản đồ. Một cách tiếp cận đặc biệt hơn đang đến với đề xuất biểu tượng riêng tư nhưng đó là một thời gian dài kể từ bây giờ.
  • Giữ dữ liệu về các đối tượng thư viện mà không thay đổi chúng hoặc phát sinh chi phí.
  • Giữ dữ liệu về một tập hợp nhỏ các đối tượng trong đó tồn tại nhiều đối tượng cùng loại để không gặp vấn đề với các lớp ẩn Công cụ JS sử dụng cho các đối tượng cùng loại.
  • Giữ dữ liệu về các đối tượng máy chủ như các nút DOM trong trình duyệt.
  • Thêm một khả năng cho một đối tượng từ bên ngoài (như ví dụ về trình phát sự kiện trong câu trả lời khác).

Hãy nhìn vào một công dụng thực sự

Nó có thể được sử dụng để mở rộng một đối tượng từ bên ngoài. Chúng ta hãy đưa ra một ví dụ thực tế (thích nghi, sắp xếp thực tế - để đưa ra quan điểm) từ thế giới thực của Node.js.

Giả sử bạn là Node.js và bạn có Promisecác đối tượng - bây giờ bạn muốn theo dõi tất cả các lời hứa hiện đang bị từ chối - tuy nhiên, bạn không muốn giữ chúng khỏi rác được thu thập trong trường hợp không có tài liệu tham khảo nào tồn tại.

Bây giờ, bạn không muốn thêm thuộc tính vào các đối tượng gốc vì những lý do rõ ràng - vì vậy bạn bị mắc kẹt. Nếu bạn giữ các tham chiếu đến những lời hứa bạn sẽ gây rò rỉ bộ nhớ vì không có bộ sưu tập rác nào có thể xảy ra. Nếu bạn không giữ tài liệu tham khảo thì bạn không thể lưu thông tin bổ sung về các lời hứa riêng lẻ. Bất kỳ kế hoạch nào liên quan đến việc lưu ID của một lời hứa vốn có nghĩa là bạn cần tham chiếu đến nó.

Nhập WeakMaps

WeakMaps có nghĩa là các phím yếu. Không có cách nào để liệt kê một bản đồ yếu hoặc để có được tất cả các giá trị của nó. Trong bản đồ yếu, bạn có thể lưu trữ dữ liệu dựa trên khóa và khi khóa được thu gom rác, hãy thực hiện các giá trị.

Điều này có nghĩa là đưa ra một lời hứa bạn có thể lưu trữ trạng thái về nó - và đối tượng đó vẫn có thể là rác được thu thập. Sau này, nếu bạn nhận được một tham chiếu đến một đối tượng, bạn có thể kiểm tra xem bạn có bất kỳ trạng thái nào liên quan đến nó và báo cáo nó không.

Điều này đã được sử dụng để thực hiện các móc từ chối chưa được xử lý bởi Petka Antonov như sau :

process.on('unhandledRejection', function(reason, p) {
    console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
    // application specific logging, throwing an error, or other logic here
});

Chúng tôi giữ thông tin về những lời hứa trong bản đồ và có thể biết khi nào một lời hứa bị từ chối được xử lý.


8
Xin chào! Bạn có thể vui lòng cho tôi biết phần nào của mã ví dụ gây rò rỉ bộ nhớ không?
ltamaj

15
@ ltamajs4 chắc chắn, trong useObjví dụ sử dụng a Mapvà không phải WeakMapchúng ta sử dụng đối tượng được truyền vào làm khóa bản đồ. Đối tượng không bao giờ bị xóa khỏi bản đồ (vì chúng ta sẽ không biết khi nào nên làm điều đó) vì vậy luôn có một tham chiếu đến nó và nó không bao giờ có thể được thu gom rác. Trong ví dụ WeakMap ngay khi tất cả các tham chiếu khác đến đối tượng không còn nữa - đối tượng có thể bị xóa khỏi WeakMap. Nếu bạn vẫn không chắc ý của tôi là gì, xin vui lòng cho tôi biết
Benjamin Gruenbaum

@Benjamin, Chúng ta cần phân biệt giữa nhu cầu về bộ đệm nhạy cảm với bộ nhớ và nhu cầu về bộ dữ liệu data_object. Đừng giới thiệu hai yêu cầu riêng biệt này. calledVí dụ của bạn được viết tốt hơn bằng cách sử dụng jsfiddle.net/f2efbm7z và nó không thể hiện việc sử dụng bản đồ yếu. Trên thực tế, nó có thể được viết tốt hơn trong tổng số 6 cách, mà tôi sẽ liệt kê dưới đây.
Pacerier

Về cơ bản, mục đích của bản đồ yếu là bộ đệm nhạy cảm với bộ nhớ. Mặc dù nó có thể được sử dụng để mở rộng các đối tượng từ bên ngoài, nhưng đó là một vụ hack tệ hại không cần thiết và chắc chắn không phải là mục đích đúng đắn của nó .
Pacerier

1
Nếu bạn muốn giữ liên kết giữa một lời hứa và số lần nó đã được xử lý / từ chối, hãy sử dụng ký hiệu 1) ; p[key_symbol] = data. hoặc 2) đặt tên độc đáo; p.__key = data. hoặc 3) phạm vi riêng tư; (()=>{let data; p.Key = _=>data=_;})(). hoặc 4) proxy với 1 hoặc 2 hoặc 3. hoặc 5) thay thế / mở rộng lớp Promise bằng 1 hoặc 2 hoặc 3. hoặc 6) thay thế / mở rộng lớp Promise với một nhóm thành viên cần thiết. - Trong mọi trường hợp, bản đồ yếu không cần thiết trừ khi bạn cần bộ đệm nhạy cảm với bộ nhớ.
Pacerier

48

Câu trả lời này dường như bị sai lệch và không sử dụng được trong một kịch bản thế giới thực. Vui lòng đọc nó và không coi đó là một lựa chọn thực tế cho bất cứ điều gì khác ngoài thử nghiệm

Một trường hợp sử dụng có thể là sử dụng nó như một từ điển cho người nghe, tôi có một đồng nghiệp đã làm điều đó. Nó rất hữu ích vì bất kỳ người nghe nào cũng được nhắm trực tiếp vào cách làm việc này. Tạm biệt listener.on.

Nhưng từ quan điểm trừu tượng hơn, WeakMapđặc biệt mạnh mẽ để phi vật chất hóa quyền truy cập vào bất cứ thứ gì, bạn không cần một không gian tên để cô lập các thành viên của nó vì nó đã được ngụ ý bởi bản chất của cấu trúc này. Tôi khá chắc chắn rằng bạn có thể thực hiện một số cải tiến bộ nhớ lớn bằng cách thay thế các khóa đối tượng dư thừa (mặc dù việc giải cấu trúc thực hiện công việc cho bạn).


Trước khi đọc những gì tiếp theo

Bây giờ tôi nhận ra rằng sự nhấn mạnh của tôi không chính xác là cách tốt nhất để giải quyết vấn đề và như Benjamin Gruenbaum đã chỉ ra (hãy xem câu trả lời của anh ấy, nếu nó chưa ở trên tôi: p), vấn đề này không thể được giải quyết một cách thường xuyên Map, vì nó sẽ bị rò rỉ, do đó, sức mạnh chính của WeakMapnó là không can thiệp vào việc thu gom rác do họ không giữ tài liệu tham khảo.


Đây là mã thực tế của đồng nghiệp của tôi (cảm ơn anh ấy đã chia sẻ)

Nguồn đầy đủ ở đây , đó là về quản lý người nghe mà tôi đã nói ở trên (bạn cũng có thể xem thông số kỹ thuật )

var listenableMap = new WeakMap();


export function getListenable (object) {
    if (!listenableMap.has(object)) {
        listenableMap.set(object, {});
    }

    return listenableMap.get(object);
}


export function getListeners (object, identifier) {
    var listenable = getListenable(object);
    listenable[identifier] = listenable[identifier] || [];

    return listenable[identifier];
}


export function on (object, identifier, listener) {
    var listeners = getListeners(object, identifier);

    listeners.push(listener);
}


export function removeListener (object, identifier, listener) {
    var listeners = getListeners(object, identifier);

    var index = listeners.indexOf(listener);
    if(index !== -1) {
        listeners.splice(index, 1);
    }
}


export function emit (object, identifier, ...args) {
    var listeners = getListeners(object, identifier);

    for (var listener of listeners) {
        listener.apply(object, args);
    }
}

2
Tôi không hiểu làm thế nào bạn sẽ sử dụng này. Nó sẽ khiến cho có thể quan sát được sụp đổ cùng với các sự kiện ràng buộc với nó khi không còn được tham chiếu. Vấn đề tôi có xu hướng gặp phải là khi Người quan sát không còn được tham chiếu nữa. Tôi nghĩ rằng giải pháp ở đây chỉ giải quyết được một nửa vấn đề. Tôi không nghĩ bạn có thể giải quyết vấn đề người quan sát với WeakMap vì nó không lặp lại được.
jgmjgm

1
Người nghe sự kiện đệm đôi có thể nhanh trong các ngôn ngữ khác, nhưng trong trường hợp này nó chỉ là bí truyền và chậm. Đó là ba xu của tôi.
Jack Giffin

@axelduch, Wow huyền thoại xử lý người nghe này đã được bán hàng trên khắp cộng đồng Javascript, đạt được 40 lượt upvote! Để hiểu lý do tại sao câu trả lời này là hoàn toàn sai , hãy xem các bình luận trong stackoverflow.com/a/156618/632951
Pacerier

1
@Pacerier đã cập nhật câu trả lời, cảm ơn vì đã phản hồi
axelduch

1
@axelduch, Yea, cũng có một ref từ đó.
Pacerier

18

WeakMap hoạt động tốt để đóng gói và ẩn thông tin

WeakMapchỉ có sẵn cho ES6 trở lên. A WeakMaplà tập hợp các cặp khóa và giá trị trong đó khóa phải là một đối tượng. Trong ví dụ sau, chúng tôi xây dựng một WeakMapvới hai mục:

var map = new WeakMap();
var pavloHero = {first: "Pavlo", last: "Hero"};
var gabrielFranco = {first: "Gabriel", last: "Franco"};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero));//This is Hero

Chúng tôi đã sử dụng set()phương thức để xác định một liên kết giữa một đối tượng và một mục khác (một chuỗi trong trường hợp của chúng tôi). Chúng tôi đã sử dụng get()phương thức để truy xuất mục được liên kết với một đối tượng. Khía cạnh thú vị của WeakMaps là thực tế là nó chứa một tham chiếu yếu đến khóa bên trong bản đồ. Tham chiếu yếu có nghĩa là nếu đối tượng bị phá hủy, trình thu gom rác sẽ xóa toàn bộ mục nhập khỏi WeakMap, do đó giải phóng bộ nhớ.

var TheatreSeats = (function() {
  var priv = new WeakMap();
  var _ = function(instance) {
    return priv.get(instance);
  };

  return (function() {
      function TheatreSeatsConstructor() {
        var privateMembers = {
          seats: []
        };
        priv.set(this, privateMembers);
        this.maxSize = 10;
      }
      TheatreSeatsConstructor.prototype.placePerson = function(person) {
        _(this).seats.push(person);
      };
      TheatreSeatsConstructor.prototype.countOccupiedSeats = function() {
        return _(this).seats.length;
      };
      TheatreSeatsConstructor.prototype.isSoldOut = function() {
        return _(this).seats.length >= this.maxSize;
      };
      TheatreSeatsConstructor.prototype.countFreeSeats = function() {
        return this.maxSize - _(this).seats.length;
      };
      return TheatreSeatsConstructor;
    }());
})()

4
"Bản đồ yếu hoạt động tốt cho việc đóng gói và che giấu thông tin". Chỉ vì bạn có thể, không có nghĩa là bạn nên. Javascript có các cách mặc định để thực hiện đóng gói và ẩn thông tin ngay cả trước khi sơ đồ yếu được phát minh. Như bây giờ, có 6 cách để làm điều đó . Sử dụng sơ đồ yếu để đóng gói là một khuôn mặt xấu xí.
Pacerier

12

𝗠𝗲𝘁𝗮𝗱𝗮𝘁𝗮

Bản đồ yếu có thể được sử dụng để lưu trữ siêu dữ liệu về các thành phần DOM mà không can thiệp vào bộ sưu tập rác hoặc khiến đồng nghiệp tức giận với mã của bạn. Ví dụ: bạn có thể sử dụng chúng để lập chỉ mục số tất cả các yếu tố trong trang web.

𝗪𝗶𝘁𝗵𝗼𝘂𝘁 𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀 𝗼𝗿:

var elements = document.getElementsByTagName('*'),
  i = -1, len = elements.length;

while (++i !== len) {
  // Production code written this poorly makes me want to cry:
  elements[i].lookupindex = i;
  elements[i].elementref = [];
  elements[i].elementref.push( elements[(i * i) % len] );
}

// Then, you can access the lookupindex's
// For those of you new to javascirpt, I hope the comments below help explain 
// how the ternary operator (?:) works like an inline if-statement
document.write(document.body.lookupindex + '<br />' + (
    (document.body.elementref.indexOf(document.currentScript) !== -1)
    ? // if(document.body.elementref.indexOf(document.currentScript) !== -1){
    "true"
    : // } else {
    "false"
  )   // }
);

𝗨𝘀𝗶𝗻𝗴 𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀 𝗮𝗻𝗱:

var DOMref = new WeakMap(),
  __DOMref_value = Array,
  __DOMref_lookupindex = 0,
  __DOMref_otherelement = 1,
  elements = document.getElementsByTagName('*'),
  i = -1, len = elements.length, cur;

while (++i !== len) {
  // Production code written this greatly makes me want to 😊:
  cur = DOMref.get(elements[i]);
  if (cur === undefined)
    DOMref.set(elements[i], cur = new __DOMref_value)

  cur[__DOMref_lookupindex] = i;
  cur[__DOMref_otherelement] = new WeakSet();
  cur[__DOMref_otherelement].add( elements[(i * i) % len] );
}

// Then, you can access the lookupindex's
cur = DOMref.get(document.body)
document.write(cur[__DOMref_lookupindex] + '<br />' + (
    cur[__DOMref_otherelement].has(document.currentScript)
    ? // if(cur[__DOMref_otherelement].has(document.currentScript)){
    "true"
    : // } else {
    "false"
  )   // }
);

𝗧𝗵𝗲 𝗗𝗶𝗳𝗳𝗲𝗿𝗲𝗻𝗰𝗲

Sự khác biệt có thể trông không đáng kể, ngoài thực tế là phiên bản yếu hơn dài hơn, tuy nhiên có một sự khác biệt lớn giữa hai đoạn mã được hiển thị ở trên. Trong đoạn mã đầu tiên, không có bản đồ yếu, đoạn mã lưu trữ các tham chiếu mọi cách giữa các phần tử DOM. Điều này ngăn các thành phần DOM khỏi rác được thu thập.(i * i) % lencó vẻ như là một trò chơi kỳ quặc mà không ai sẽ sử dụng, nhưng hãy nghĩ lại: rất nhiều mã sản xuất có các tham chiếu DOM nảy trên toàn bộ tài liệu. Bây giờ, đối với đoạn mã thứ hai, vì tất cả các tham chiếu đến các phần tử đều yếu, khi bạn loại bỏ một nút, trình duyệt có thể xác định rằng nút đó không được sử dụng (mã của bạn không thể truy cập được) do đó xóa nó khỏi bộ nhớ. Lý do tại sao bạn nên quan tâm đến việc sử dụng bộ nhớ và neo bộ nhớ (những thứ như đoạn mã đầu tiên chứa các phần tử không được sử dụng trong bộ nhớ) là vì sử dụng nhiều bộ nhớ hơn có nghĩa là nhiều lần thử trình duyệt hơn (để cố gắng giải phóng bộ nhớ ngăn chặn sự cố trình duyệt) có nghĩa là trải nghiệm duyệt web chậm hơn và đôi khi sự cố trình duyệt.

Đối với một polyfill cho những điều này, tôi muốn giới thiệu thư viện của riêng tôi ( tìm thấy ở đây @ github ). Đây là một thư viện rất nhẹ, đơn giản sẽ tạo ra nó mà không có bất kỳ khuôn khổ nào quá phức tạp mà bạn có thể tìm thấy trong các polyfill khác.

~ Chúc mừng mã hóa!


1
Cảm ơn đã giải thích rõ ràng. Một ví dụ đáng giá hơn bất kỳ từ nào.
newguy ngày

@lolzery, Re " Điều này ngăn các thành phần DOM khỏi rác được thu thập ", tất cả những gì bạn cần là đặt elementsthành null và bạn đã hoàn tất: Nó sẽ được xử lý. & Re "Các tham chiếu DOM nảy trên toàn bộ tài liệu ", hoàn toàn không thành vấn đề: Một khi liên kết chính elementskhông còn nữa, tất cả các tham chiếu vòng tròn sẽ được cung cấp. Nếu phần tử của bạn đang giữ các tham chiếu đến phần tử mà nó không cần, thì hãy sửa mã và đặt ref thành null khi bạn hoàn thành việc sử dụng nó. Nó sẽ được cấp bằng. Weakmaps không cần thiết .
Pacerier

2
@Pacerier cảm ơn bạn đã phản hồi nhiệt tình, tuy nhiên, việc đặt elementsthành null sẽ không cho phép trình duyệt xử lý các yếu tố trong tình huống đoạn đầu tiên. Điều này là do bạn đặt các thuộc tính tùy chỉnh trên các thành phần và sau đó các yếu tố đó vẫn có thể được lấy và các thuộc tính tùy chỉnh của chúng vẫn có thể được truy cập, do đó ngăn không cho bất kỳ yếu tố nào trong số chúng bị GC. Hãy nghĩ về nó giống như một chuỗi các vòng kim loại. Solongas bạn có quyền truy cập vào ít nhất một liên kết trong chuỗi, bạn có thể giữ liên kết đó trong chuỗi và do đó ngăn toàn bộ chuỗi vật phẩm rơi xuống vực thẳm.
Jack Giffin

1
mã sản xuất với dunder tên vars làm tôi nôn mửa
Barbu Barbu

10

tôi sử dụng WeakMap cho bộ nhớ cache của việc ghi nhớ miễn phí các hàm lấy các đối tượng bất biến làm tham số của chúng.

Ghi nhớ là cách nói thú vị "sau khi bạn tính toán giá trị, hãy lưu nó vào bộ đệm để bạn không phải tính toán lại".

Đây là một ví dụ:

Một số điều cần lưu ý:

  • Các đối tượng Immutable.js trả về các đối tượng mới (với một con trỏ mới) khi bạn sửa đổi chúng để sử dụng chúng làm khóa trong WeakMap sẽ đảm bảo cùng một giá trị được tính toán.
  • WeakMap rất tốt cho các bản ghi nhớ vì một khi đối tượng (được sử dụng làm khóa) nhận được rác được thu thập, thì giá trị tính toán trên WeakMap cũng vậy.

1
Đây là cách sử dụng hợp lệ của sơ đồ yếu miễn là bộ nhớ đệm ghi nhớ có nghĩa là nhạy cảm với bộ nhớ , không tồn tại trong suốt vòng đời obj / chức năng. Nếu "bộ nhớ cache ghi nhớ" có nghĩa là tồn tại trong suốt vòng đời obj / function, thì sơ đồ yếu là lựa chọn sai: Thay vào đó, sử dụng bất kỳ 6 kỹ thuật đóng gói javascript mặc định nào .
Pacerier

3

Tôi có tính năng đơn giản dựa trên trường hợp sử dụng / Ví dụ cho WeakMaps.

QUẢN LÝ MỘT BỘ SƯU TẬP

Tôi bắt đầu với một Userđối tượng có tính chất bao gồm fullname, username, age, gendervà một phương pháp gọi printđó in một bản tóm tắt có thể đọc được con người của các tài sản khác.

/**
Basic User Object with common properties.
*/
function User(username, fullname, age, gender) {
    this.username = username;
    this.fullname = fullname;
    this.age = age;
    this.gender = gender;
    this.print = () => console.log(`${this.fullname} is a ${age} year old ${gender}`);
}

Sau đó tôi đã thêm một Bản đồ được gọi usersđể giữ một bộ sưu tập nhiều người dùng được khóa bởi username.

/**
Collection of Users, keyed by username.
*/
var users = new Map();

Ngoài ra, Bộ sưu tập cũng yêu cầu các chức năng của trình trợ giúp để thêm, nhận, xóa Người dùng và thậm chí là một chức năng để in tất cả người dùng vì sự hoàn chỉnh.

/**
Creates an User Object and adds it to the users Collection.
*/
var addUser = (username, fullname, age, gender) => {
    let an_user = new User(username, fullname, age, gender);
    users.set(username, an_user);
}

/**
Returns an User Object associated with the given username in the Collection.
*/
var getUser = (username) => {
    return users.get(username);
}

/**
Deletes an User Object associated with the given username in the Collection.
*/
var deleteUser = (username) => {
    users.delete(username);
}

/**
Prints summary of all the User Objects in the Collection.
*/
var printUsers = () => {
    users.forEach((user) => {
        user.print();
    });
}

Với tất cả các mã trên đang chạy, giả sử NodeJS , chỉ có usersBản đồ có tham chiếu đến Đối tượng người dùng trong toàn bộ quá trình. Không có tài liệu tham khảo nào khác cho các Đối tượng Người dùng riêng lẻ.

Chạy mã này một trình bao NodeJS tương tác, giống như một ví dụ Tôi thêm bốn người dùng và in chúng: Thêm và in người dùng

THÊM THÊM THÔNG TIN CHO NGƯỜI SỬ DỤNG MÀ KHÔNG CẦN SỬA ĐỔI MÃ SỐ HIỆN TẠI

Bây giờ nói rằng một tính năng mới là bắt buộc trong đó mỗi liên kết Nền tảng truyền thông xã hội (SMP) của người dùng cần được theo dõi cùng với Đối tượng người dùng.

Chìa khóa ở đây cũng là tính năng này phải được thực hiện với sự can thiệp tối thiểu vào mã hiện có.

Điều này là có thể với WeakMaps theo cách sau.

Tôi thêm ba WeakMaps riêng cho Twitter, Facebook, LinkedIn.

/*
WeakMaps for Social Media Platforms (SMPs).
Could be replaced by a single Map which can grow
dynamically based on different SMP names . . . anyway...
*/
var sm_platform_twitter = new WeakMap();
var sm_platform_facebook = new WeakMap();
var sm_platform_linkedin = new WeakMap();

Một hàm trợ giúp, getSMPWeakMapđược thêm vào đơn giản để trả về WeakMap được liên kết với tên SMP đã cho.

/**
Returns the WeakMap for the given SMP.
*/
var getSMPWeakMap = (sm_platform) => {
    if(sm_platform == "Twitter") {
        return sm_platform_twitter;
    }
    else if(sm_platform == "Facebook") {
        return sm_platform_facebook;
    }
    else if(sm_platform == "LinkedIn") {
        return sm_platform_linkedin;
    }
    return undefined;
}

Một chức năng để thêm liên kết SMP của người dùng vào WeakMap SMP đã cho.

/**
Adds a SMP link associated with a given User. The User must be already added to the Collection.
*/
var addUserSocialMediaLink = (username, sm_platform, sm_link) => {
    let user = getUser(username);
    let sm_platform_weakmap = getSMPWeakMap(sm_platform);
    if(user && sm_platform_weakmap) {
        sm_platform_weakmap.set(user, sm_link);
    }
}

Một chức năng chỉ in những người dùng có mặt trên SMP đã cho.

/**
Prints the User's fullname and corresponding SMP link of only those Users which are on the given SMP.
*/
var printSMPUsers = (sm_platform) => {
    let sm_platform_weakmap = getSMPWeakMap(sm_platform);
    console.log(`Users of ${sm_platform}:`)
    users.forEach((user)=>{
        if(sm_platform_weakmap.has(user)) {
            console.log(`\t${user.fullname} : ${sm_platform_weakmap.get(user)}`)
        }
    });
}

Bây giờ bạn có thể thêm các liên kết SMP cho người dùng, với khả năng mỗi người dùng có một liên kết trên nhiều SMP.

... Tiếp tục với ví dụ trước đó, tôi thêm các liên kết SMP cho người dùng, nhiều liên kết cho người dùng Bill và Sarah và sau đó in các liên kết cho từng SMP riêng biệt: Thêm liên kết SMP cho người dùng và hiển thị chúng

Bây giờ hãy nói một Người dùng bị xóa khỏi usersBản đồ bằng cách gọi deleteUser. Điều đó loại bỏ tham chiếu duy nhất đến Đối tượng người dùng. Đến lượt nó cũng sẽ xóa liên kết SMP khỏi bất kỳ / tất cả các WeakMaps SMP (của Bộ sưu tập rác) vì không có Đối tượng người dùng, không có cách nào để truy cập bất kỳ liên kết SMP nào của nó.

... Tiếp tục với Ví dụ, tôi xóa Bill người dùng và sau đó in ra các liên kết của SMP mà anh ta được liên kết với:

Xóa hóa đơn người dùng khỏi Bản đồ cũng xóa các liên kết SMP

Không có yêu cầu về bất kỳ mã bổ sung nào để xóa riêng liên kết SMP và mã hiện có trước khi tính năng này không được sửa đổi.

Nếu có bất kỳ cách nào khác để thêm tính năng này có / không có WeakMaps, vui lòng bình luận.


_____nice____
Aleks
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.