440

What are the actual uses of the WeakMap data structure introduced in ECMAScript 6?

Since a key of a weak map creates a strong reference to its corresponding value, ensuring that a value which has been inserted into a weak map will never disappear as long as its key is still alive, it can't be used for memo tables, caches or anything else that you would normally use weak references, maps with weak values, etc. for.

It seems to me that this:

weakmap.set(key, value);

...is just a roundabout way of saying this:

key.value = value;

What concrete use cases am I missing?

valderman
  • 7,295
  • 4
  • 17
  • 29
  • 10
    [A blog post.](http://fitzgeraldnick.com/weblog/53/) – Pointy Apr 02 '15 at 12:50
  • 2
    And another one -- http://ilikekillnerds.com/2015/02/what-are-weakmaps-in-es6/ – James Sumners Apr 02 '15 at 13:02
  • 42
    Real world use case: Store custom data for DOM nodes. – Felix Kling Apr 02 '15 at 14:02
  • All the use cases you mention for weak references are super important too. They're just a lot harder to add to the language since they introduce nondeterminism. Mark Miller and others have done a lot of work on weak references and I _think_ they're eventually coming. Eventually – Benjamin Gruenbaum Apr 02 '15 at 15:34
  • @Pointy: your "This can balloon your memory footprint" comment in that post is bogus. Closing over data should take the same memory as adding a reference to a map. That, or get a refund. – Eli Barzilay Jul 08 '15 at 06:18
  • @EliBarzilay wow I a had to check that; I'm glad I didn't write it :) I agree; I suspect that function instances (distinct from function *code*) are probably fairly cheap. (The author was, I think, talking about the wastefulness of having the methods returned from that closure, not the closure itself.) – Pointy Jul 08 '15 at 12:07
  • @Pointy: Ah, sorry, I didn't realize that it's someone else (and there are no comments, and I'm too lazy to go via some github issue). And yes, a function closure (which I'm guessing is what you call an instance) is basically a pointer to its precompiled code + a pointer to values it closes over -- so there's no extra cost over any other way to store some value. – Eli Barzilay Jul 08 '15 at 21:34
  • @EliBarzilay [Actually....](http://mrale.ph/blog/2012/09/23/grokking-v8-closures-for-fun.html) but yeah that post is pretty bogus. – Benjamin Gruenbaum Jul 21 '15 at 17:03
  • @BenjaminGruenbaum: My "or get a refund" is saying that the cost *should* be the same (up to constants, as usual) -- it *might* not be the case, but that means that there's a missing optimization. By complete coincidence, just last night I talked to someone about closing over variables that hold big values: setting them to (eg) `null` when the value is no longer needed is something that you should generally avoid since that's the language's responsibility (for languages that have a GC)... – Eli Barzilay Jul 22 '15 at 08:55
  • http://blog.garstasio.com/you-dont-need-jquery/utils/#associate-data-with-an-html-element – Ray Nicholus Sep 19 '15 at 02:30
  • You can have a look at following code in [polymer](https://github.com/Polymer/polymer/blob/master/src/lib/collection.html#L29). – Ajinkya Nov 23 '15 at 15:29
  • Use case: private instance members http://simple.gy/blog/js-private-properties – SimplGy Sep 17 '16 at 03:21
  • Java implemented weak references 17 years earlier (v1.2 in 1998), so there are more explanations available in Java land. For example: https://web.archive.org/web/20061130103858/http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html – Nayuki Sep 18 '19 at 19:13
  • @Nayuki: in a JS `WeakMap` the key keeps the value alive, unlike weak references in Java, Haskell, and most other languages that have them. They are very different things. – valderman Sep 19 '19 at 10:27
  • @valderman That's fine and good, because the article I linked to talks about [WeakHashMap](https://docs.oracle.com/javase/8/docs/api/java/util/WeakHashMap.html) usage several paragraphs down in the section "When strong references are too strong". – Nayuki Sep 19 '19 at 16:51
  • 2
    `WeakMap`s can be used to detect memory leaks: http://stevehanov.ca/blog/?id=148 – happybeing May 27 '20 at 11:19

7 Answers7

568

Fundamentally

WeakMaps provide a way to extend objects from the outside without interfering with garbage collection. Whenever you want to extend an object but can't because it is sealed - or from an external source - a WeakMap can be applied.

A WeakMap is a map (dictionary) where the keys are weak - that is, if all references to the key are lost and there are no more references to the value - the value can be garbage collected. Let's show this first through examples, then explain it a bit and finally finish with real use.

Let's say I'm using an API that gives me a certain object:

var obj = getObjectFromLibrary();

Now, I have a method that uses the object:

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

I want to keep track of how many times the method was called with a certain object and report if it happens more than N times. Naively one would think to use a Map:

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

This works, but it has a memory leak - we now keep track of every single library object passed to the function which keeps the library objects from ever being garbage collected. Instead - we can use a 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);
}

And the memory leak is gone.

Use cases

Some use cases that would otherwise cause a memory leak and are enabled by WeakMaps include:

  • Keeping private data about a specific object and only giving access to it to people with a reference to the Map. A more ad-hoc approach is coming with the private-symbols proposal but that's a long time from now.
  • Keeping data about library objects without changing them or incurring overhead.
  • Keeping data about a small set of objects where many objects of the type exist to not incur problems with hidden classes JS engines use for objects of the same type.
  • Keeping data about host objects like DOM nodes in the browser.
  • Adding a capability to an object from the outside (like the event emitter example in the other answer).

Let's look at a real use

It can be used to extend an object from the outside. Let's give a practical (adapted, sort of real - to make a point) example from the real world of Node.js.

Let's say you're Node.js and you have Promise objects - now you want to keep track of all the currently rejected promises - however, you do not want to keep them from being garbage collected in case no references exist to them.

Now, you don't want to add properties to native objects for obvious reasons - so you're stuck. If you keep references to the promises you're causing a memory leak since no garbage collection can happen. If you don't keep references then you can't save additional information about individual promises. Any scheme that involves saving the ID of a promise inherently means you need a reference to it.

Enter WeakMaps

WeakMaps mean that the keys are weak. There are no ways to enumerate a weak map or to get all its values. In a weak map, you can store the data based on a key and when the key gets garbage collected so do the values.

This means that given a promise you can store state about it - and that object can still be garbage collected. Later on, if you get a reference to an object you can check if you have any state relating to it and report it.

This was used to implement unhandled rejection hooks by Petka Antonov as this:

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

We keep information about promises in a map and can know when a rejected promise was handled.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 246,787
  • 79
  • 474
  • 476
  • 9
    Hello! Could you please tell me which part of the example code causes memory leak? – ltamajs Oct 28 '16 at 13:17
  • 18
    @ltamajs4 sure, in the `useObj` example using a `Map` and not a `WeakMap` we use the passed in object as a map key. The object is never removed from the map (since we wouldn't know when to do that) so there is always a reference to it and it can never be garbage collected. In the WeakMap example as soon as all other references to the object are gone - the object can be cleared from the `WeakMap`. If you're still not sure what I mean please let me know – Benjamin Gruenbaum Oct 28 '16 at 13:56
  • 1
    @Benjamin, We need to distinguish between the need for a memory-sensitive cache and the need for a data_object tuple. **Do not conflate these two separate requirements.** Your `called` example is better written using https://jsfiddle.net/f2efbm7z/ and it does not demonstrate the use of a weak map. In fact, it can be better written in a total of **6** ways, which I'll list below. – Pacerier Sep 18 '17 at 18:26
  • 1
    Fundamentally, the purpose of weak map is a memory-sensitive cache. While it can be used to extend objects from the outside, **that is an undeeded lousy hack and is definitely not its proper purpose**. – Pacerier Sep 18 '17 at 18:27
  • 1
    Re "*let's look at a real use...keep information about promises in a map*", I do not see why the `promise_to_exception_Map` needs to be weak: Why not remove entries in the map manually after you fire "rejectionHandled"? That's a much much better approach. – Pacerier Sep 18 '17 at 18:32
  • 1
    If you want to keep the link between a promise and the number of times it has been handled/rejected, use **1)** symbol; `p[key_symbol] = data`. or **2)** unique naming; `p.__key = data`. or **3)** private scope; `(()=>{let data; p.Key = _=>data=_;})()`. or **4)** proxy with 1 or 2 or 3. or **5)** replace/extend Promise class with 1 or 2 or 3. or **6)** replace/extend Promise class with a tuple of needed members. — In any case, **weak map not needed** unless you need a memory sensitive cache. – Pacerier Sep 18 '17 at 18:32
  • 1
    @Pacerier Sorry just saw this. re: the jsfiddle - that requires modifying the original object (and indeed how weakmaps are polyfilled). That is not possible in all cases: For example if you are a library doing instrumentation then you can't modify the object - Node.js can't alter every single promise like that and it won't work if someone does `Object.getOwnPropertySymbols` on the object. As a fun fact - if you override `Object.getOwnPropertySymbols` then it is possible (and indeed how weakmaps ar polyfilled) but that won't work out of the box and libraries don't like overriding natives :) – Benjamin Gruenbaum Jul 22 '18 at 17:58
  • 1
    @Pacerier to clarify: you are right and WeakMaps can be polyfilled if you allow modifying all the built-in globals and ways to access a property. In fact this is what [the polyfill actually does](https://github.com/drses/weak-map/blob/master/weak-map.js#L247-L251). That said - libraries generally cannot modify builtins and thus a solution from the platform enables the use case. If you are not a library and can count on the object not being observed at all then you do not need a WeakMap. – Benjamin Gruenbaum Jul 22 '18 at 18:04
48

This answer seems to be biased and unusable in a real world scenario. Please read it as is, and don't consider it as an actual option for anything else than experimentation

A use case could be to use it as a dictionary for listeners, I have a coworker who did that. It is very helpful because any listener is directly targetted with this way of doing things. Goodbye listener.on.

But from a more abstract point of view, WeakMap is especially powerful to dematerialize access to basically anything, you don't need a namespace to isolate its members since it is already implied by the nature of this structure. I'm pretty sure you could do some major memory improvements by replacing awkwards redundant object keys (even though deconstructing does the work for you).


Before reading what is next

I do now realize my emphasize is not exactly the best way to tackle the problem and as Benjamin Gruenbaum pointed out (check out his answer, if it's not already above mine :p), this problem could not have been solved with a regular Map, since it would have leaked, thus the main strength of WeakMap is that it does not interfere with garbage collection given that they do not keep a reference.


Here is the actual code of my coworker (thanks to him for sharing)

Full source here, it's about listeners management I talked about above (you can also take a look at the specs)

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);
    }
}
Community
  • 1
  • 1
axelduch
  • 10,132
  • 2
  • 26
  • 49
  • 2
    I don't quite get how you would use this. It would cause the observable to collapse along with events bound to it when no longer referenced. The problem I tend to have is when the Observer is no longer referenced. I think the solution here only solved half the problem. I don't think you can solve the observer problem with WeakMap as it isn't iterable. – jgmjgm Jun 06 '16 at 14:55
  • 1
    Double-buffering event listeners may be fast in other languages, but in this case it is just plain esoteric and slow. That's my three-cents. – Jack Giffin Sep 05 '17 at 23:54
  • @axelduch, Wow this listener-handle myth has been peddled all the way to the Javascript community, gaining 40 upvotes! To understand why **this answer is completely wrong**, see comments under https://stackoverflow.com/a/156618/632951 – Pacerier Sep 18 '17 at 18:42
  • 1
    @Pacerier updated the answer, thanks for the feedback – axelduch Sep 18 '17 at 18:50
  • @Pacerier also your link is not the right one, I think you meant https://stackoverflow.com/questions/154724/when-would-you-use-a-weakhashmap-or-a-weakreference/25241986#comment56035762_25241986 – axelduch Sep 18 '17 at 18:54
  • 1
    @axelduch, Yea, there's a ref from there too. – Pacerier Sep 18 '17 at 18:55
23

WeakMap works well for encapsulation and information hiding

WeakMap is only available for ES6 and above. A WeakMap is a collection of key and value pairs where the key must be an object. In the following example, we build a WeakMap with two items:

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

We used the set() method to define an association between an object and another item (a string in our case). We used the get() method to retrieve the item associated with an object. The interesting aspect of the WeakMaps is the fact that it holds a weak reference to the key inside the map. A weak reference means that if the object is destroyed, the garbage collector will remove the entire entry from the WeakMap, thus freeing up memory.

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;
    }());
})()
Yangshun Tay
  • 33,183
  • 26
  • 102
  • 123
Michael Horojanski
  • 3,617
  • 4
  • 27
  • 32
  • 4
    Re "weakmap work well for encapsulation and information hiding". Just because you can, doesn't mean you should. Javascript has default ways of doing encapsulation and information hiding even before weakmap was invented. As at now, there are [literally 6 ways to do it](https://stackoverflow.com/questions/29413222/what-are-the-actual-uses-of-es6-weakmap/40241741#comment79534889_29416340). Using weakmap to do encapsulation is an ugly facepalm. – Pacerier Sep 18 '17 at 19:41
15

Weak Maps can be used to store metadata about DOM elements without interfering with garbage collection or making coworkers mad at your code. For example, you could use them to numerically index all of the elements in a webpage.

:

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 well 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"
  )   // }
);

The difference may look negligible, aside from the fact that the weakmap version is longer, however there is a major difference between the two pieces of code shown above. In the first snippet of code, without weak maps, the piece of code stores references every which way between the DOM elements. This prevents the DOM elements from being garbage collected. (i * i) % len may seem like an oddball that nobody would use, but think again: plenty of production code has DOM references that bounce all over the document. Now, for the second piece of code, because all the references to the elements are weak, when you a remove a node, the browser is able to determine that the node is not used (not able to be reached by your code), and thus delete it from memory. The reason for why you should be concerned about memory usage, and memory anchors (things like the first snippet of code where unused elements are held in memory) is because more memory usage means more browser GC-attempts (to try to free up memory to avert a browser crash) means slower browsing experience and sometimes a browser crash.

As for a polyfill for these, I would recommend my own library (found here @ github). It is a very lightweight library that will simply polyfill it without any of the way-overly-complex frameworks you might find in other polyfills.

~ Happy coding!

Jack Giffin
  • 3,108
  • 2
  • 26
  • 45
  • 1
    Thanks for the clear explanation. An example worths more than any words. – newguy Aug 08 '17 at 04:09
  • @lolzery, Re "*This prevents the DOM elements from being garbage collected*", all you need is to **set `elements` to null** and you're done: It'll be GCed. **&** Re "*DOM references that bounce all over the document*", doesn't matter at all: Once the main link `elements` is gone, all the circular ref will be GCed. If your element is holding on references to element that it doesn't need, then fix the code and **set the ref to null** when you're done with using it. It'll be GCed. **Weakmaps not needed**. – Pacerier Sep 18 '17 at 20:45
  • 4
    @Pacerier thank you for your enthusiastic feedback, however setting `elements` to null will **not** allow the browser to GC the elements in the first snippet situation. This is because you set custom properties on the elements, and then those elements can still be obtained, and their custom properties can still be accessed, thus preventing any of them from being GC'ed. Think of it like a chain of metal rings. Solongas you have access to at least one link in the chain, you can hold onto that link in the chain, and thus prevent the entire chain of items from falling into the abyss. – Jack Giffin Sep 19 '17 at 01:21
  • 1
    production code with dunder named vars makes me vomit – Barbu Barbu Feb 16 '18 at 09:32
  • That ternary looks quite verbose to me. If you need a string representation of a boolean you can do `\`${true}\` // "true"` – Lukasz Matysiak Aug 04 '20 at 16:22
  • 2
    @LukaszMatysiak Here's a shorter and more cross-browser version: `""+true`. I won't make this change to the code because the goal of the code is to be human-readable instead of maximally space-efficient. Not everyone knows JS as well as you and I. There exist beginners who are just trying to get started with the language. It doesn't help them one bit when we flaunt our advanced knowledge of JS. – Jack Giffin Aug 05 '20 at 12:28
14

I use WeakMap for the cache of worry-free memoization of functions that take in immutable objects as their parameter.

Memoization is fancy way of saying "after you compute the value, cache it so you don't have to compute it again".

Here's an example:

// using immutable.js from here https://facebook.github.io/immutable-js/

const memo = new WeakMap();

let myObj = Immutable.Map({a: 5, b: 6});

function someLongComputeFunction (someImmutableObj) {
  // if we saved the value, then return it
  if (memo.has(someImmutableObj)) {
    console.log('used memo!');
    return memo.get(someImmutableObj);
  }
  
  // else compute, set, and return
  const computedValue = someImmutableObj.get('a') + someImmutableObj.get('b');
  memo.set(someImmutableObj, computedValue);
  console.log('computed value');
  return computedValue;
}


someLongComputeFunction(myObj);
someLongComputeFunction(myObj);
someLongComputeFunction(myObj);

// reassign
myObj = Immutable.Map({a: 7, b: 8});

someLongComputeFunction(myObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script>

A few things to note:

  • Immutable.js objects return new objects (with a new pointer) when you modify them so using them as keys in a WeakMap is guarantees the same computed value.
  • The WeakMap is great for memos because once the object (used as the key) gets garbage collected, so does the computed value on the WeakMap.
Pacerier
  • 76,400
  • 86
  • 326
  • 602
Rico Kahler
  • 12,583
  • 9
  • 41
  • 63
  • 1
    This is a valid use of weakmap as long as the memoization cache is meant to be **memory-sensitive**, not persistent throughout the obj/function life. If the "memoization cache" is meant to be persistent throughout the obj/function life, then weakmap is the wrong choice: Use [any of the 6 default javascript encapsulation techniques](https://stackoverflow.com/questions/29413222/what-are-the-actual-uses-of-es6-weakmap/40241741#comment79535070_29416340) instead. – Pacerier Sep 18 '17 at 20:55
6

I have this simple feature based use case/Example for WeakMaps.

MANAGE A COLLECTION OF USERS

I started off with a User Object whose properties include a fullname, username, age, gender and a method called print which prints a human readable summary of the other properties.

/**
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}`);
}

I then added a Map called users to keep a collection of multiple users which are keyed by username.

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

Addition of the Collection also required helper functions to add, get, delete a User and even a function to print all the users for sake of completeness.

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

With all of the above code running in, say NodeJS, only the users Map has the reference to the User Objects within the entire process. There is no other reference to the individual User Objects.

Running this code an interactive NodeJS shell, just as an Example I add four users and print them: Adding and printing users

ADD MORE INFO TO USERS WITHOUT MODIFYING EXISTING CODE

Now say a new feature is required wherein each users Social Media Platform (SMP) links need to be tracked along with the User Objects.

The key here is also that this feature must be implemented with minimum intervention to the existing code.

This is possible with WeakMaps in the following manner.

I add three separate WeakMaps for 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();

A helper function, getSMPWeakMap is added simply to return the WeakMap associated with the given SMP name.

/**
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;
}

A function to add a users SMP link to the given SMP WeakMap.

/**
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);
    }
}

A function to print only the users who are present on the given SMP.

/**
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)}`)
        }
    });
}

You can now add SMP links for the users, also with the possibility of each user having a link on multiple SMPs.

...continuing with the earlier Example, I add SMP links to the users, multiple links for users Bill and Sarah and then print the links for each SMP separately: Adding SMP links to the users and displaying them

Now say a User is deleted from the users Map by calling deleteUser. That removes the only reference to the User Object. This in turn will also clear out the SMP link from any/all of the SMP WeakMaps (by Garbage Collection) as without the User Object there is no way to access any of its SMP link.

...continuing with the Example, I delete user Bill and then print out the links of the SMPs he was associated with:

Deleting user Bill from the Map removes the SMP links as well

There is no requirement of any additional code to individually delete the SMP link separately and the existing code before this feature was not modified in anyway.

If there is any other way to add this feature with/without WeakMaps please feel free to comment.

electrocrat
  • 356
  • 2
  • 7
0

I think it's very helpful for checking a connection income in applications socket. The other case, the 'Weak Collection' are useful: https://javascript.info/task/recipients-read