99

Looking this and this MDN pages it seems like the only difference between Maps and WeakMaps is a missing "size" property for WeakMaps. But is this true? What's the difference between them?

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
Dmitrii Sorin
  • 3,639
  • 3
  • 28
  • 39
  • The effect is on the GC. WeakMaps can have their keys collected. – John Dvorak Mar 24 '13 at 21:35
  • @JanDvorak there's no example pointed on MDN about it. Like aWeakMap.get(key); // say, 2 ...(GC action)... aWeakMap.get(key); // say, undefined – Dmitrii Sorin Mar 25 '13 at 04:34
  • 1
    Your example is impossible. `key` cannot be collected, because it's referenced by you. – John Dvorak Mar 25 '13 at 04:43
  • @JanDvorak i just pointed out that there's no example on MDN about GC actions regarding WeakMaps and Maps. GC will clear memory for deleted keys for both of them, won't it? – Dmitrii Sorin Mar 25 '13 at 05:55
  • 1
    The design decision is that GC actions are invisible in Javascript. You can't observe GC doing its thing. – John Dvorak Mar 25 '13 at 05:58
  • The only difference is the browser's memory usage if only `weakMap` methods are used on `map`. – John Dvorak Mar 25 '13 at 05:58
  • 1
    See [**this related answer**](http://stackoverflow.com/a/29416340/1348195) for more information about this problem. – Benjamin Gruenbaum Aug 16 '15 at 17:22
  • I would like it if someone could explain whether there are performance differences between `Map` and `WeakMap`. This was [asked elsewhere](https://stackoverflow.com/questions/48096917/es6-maps-vs-weakmaps-is-there-any-difference-with-respect-to-speed-performance?noredirect=1&lq=1), but that question was closed as a duplicate of this one. However, none of the current answers to this question address performance. – qntm Feb 18 '18 at 22:09

7 Answers7

102

They both behave differently when a object referenced by their keys/values gets deleted. Lets take the below example code:

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

The above IIFE is executed there is no way we can reference {x: 12} and {y: 12} anymore. Garbage collector goes ahead and deletes the key b pointer from “WeakMap” and also removes {y: 12} from memory. But in case of “Map”, the garbage collector doesn’t remove a pointer from “Map” and also doesn’t remove {x: 12} from memory.

Summary: WeakMap allows garbage collector to do its task but not Map.

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

kshirish
  • 1,273
  • 1
  • 10
  • 11
  • 15
    Why is it not removed from memory? Because you can still reference it! `map.entries().next().value // [{x:12}, 1]` – Bergi May 29 '15 at 02:42
  • 4
    It's not a self invoked function. It's an immediately invoked function expression. http://benalman.com/news/2010/11/immediately-invoked-function-expression/ – Olson.dev Jul 21 '15 at 14:53
  • then what's a difference between weakmap and object – Muhammad Umer Aug 12 '16 at 15:37
  • 1
    @MuhammadUmer: object can only have string ‘keys’, while `WeakMap` can only have non-primitive keys (no strings or numbers or `Symbol`s as keys, only arrays, objects, other maps, etc.). – Ahmed Fasih Aug 28 '16 at 05:41
  • *"there is no way we can reference `{x: 12}` and `{y: 12}` anymore."* - But `{x: 12}` is still in the `Map`, as you go on to explain. – nnnnnn May 05 '17 at 07:27
  • 1
    @nnnnnn Yes that's the difference, it's still in the `Map` but not in the `WeakMap` – Alexander Derck Aug 02 '17 at 09:58
  • This answer is confusing because of the keys you have chosen. When you think of key in js you traditionally think it must be a string. It would be better to use keys such as `{}` or `function() {}`. – vaughan Nov 05 '17 at 16:27
81

Maybe the next explanation will be more clear for someone.

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

As you see, after removing k1 key from the memory we can still access it inside the map. At the same time removing k2 key of WeakMap removes it from wm as well by reference.

That's why WeakMap hasn't enumerable methods like forEach, because there is no such thing as list of WeakMap keys, they are just references to another objects.

Rax Wunter
  • 2,400
  • 3
  • 22
  • 28
55

From the very same page, section "Why Weak Map?":

The experienced JavaScript programmer will notice that this API could be implemented in JavaScript with two arrays (one for keys, one for values) shared by the 4 API methods. Such an implementation would have two main inconveniences. The first one is an O(n) search (n being the number of keys in the map). The second one is a memory leak issue. With manually written maps, the array of keys would keep references to key objects, preventing them from being garbage collected. In native WeakMaps, references to key objects are held "weakly", which means that they do not prevent garbage collection in case there would be no other reference to the object.

Because of references being weak, WeakMap keys are not enumerable (i.e. there is no method giving you a list of the keys). If they were, the list would depend on the state of garbage collection, introducing non-determinism.

[And that's why they have no size property as well]

If you want to have a list of keys, you should maintain it yourself. There is also an ECMAScript proposal aiming at introducing simple sets and maps which would not use weak references and would be enumerable.

‐ which would be the "normal" Maps. Not mentioned at MDN, but in the harmony proposal, those also have items, keys and values generator methods and implement the Iterator interface.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • so `new Map().get(x)` has about the same look-up time as reading a property from a plain object? – Alexander Mills May 05 '18 at 18:41
  • 1
    @AlexanderMills I don't see what this has to do with the question, but [here is some data](https://stackoverflow.com/a/37994079/1048572). In general, [yes they are similar](https://stackoverflow.com/a/34292923/1048572), and [you should use the appropriate one](https://stackoverflow.com/a/32604899/1048572). – Bergi May 05 '18 at 18:48
  • So my understanding is Map maintains an internal array to persist its key because of that array. Garbage collector is not able to refrain the reference. In WeekMap, it doesnt have a array where keys maintained so key with no reference can be garbage collected. – Mohan Ram May 17 '18 at 12:03
  • @MohanRam A `WeakMap` still has an array (or other collection) of entries, it just tells the garbage collector that those are [weak references](https://en.wikipedia.org/wiki/Weak_reference). – Bergi May 17 '18 at 12:41
  • Then why iteration for WeekMap keys are not supported? – Mohan Ram May 17 '18 at 12:42
  • @MohanRam Check the second paragraph in the first quote in the answer – Bergi May 17 '18 at 12:45
  • @Bergi I am able understand week reference but how compiler treats it as week reference is the one which i am not getting. Please share material which helps me to understand how compiler knows week reference. thanks for your help – Mohan Ram May 17 '18 at 12:49
34

Another difference (source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap):

Keys of WeakMaps are of the type Object only. Primitive data types as keys are not allowed (e.g. a Symbol can't be a WeakMap key).

Nor can a string, number, or boolean be used as a WeakMap key. A Map can use primitive values for keys.

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

m = new Map
m.set('a', 'b'); // Works
wavelight
  • 13
  • 3
Trevor Dixon
  • 17,772
  • 8
  • 66
  • 100
  • 6
    In case anyone wonders: I can imagine the reason behind this is: you can't keep or pass references to primitive types. So the key in a WeakMap would be its only reference, ever. That way garbage collection wouldn't be possible. I don't know if weak references are impossible or just don't make sense. But either way the key needs to be something that can be referenced weakly. – Andreas Linnert Jan 08 '16 at 14:00
5

From Javascript.info

Map -- If we use an object as the key in a regular Map, then while the Map exists, that object exists as well. It occupies memory and may not be garbage collected.

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]

Similar to that, if we use an object as the key in a regular Map, then while the Map exists, that object exists as well. It occupies memory and may not be garbage collected

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 -- Now, if we use an object as the key in it, and there are no other references to that object – it will be removed from memory (and from the map) automatically.

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

// john is removed from memory!
Avadhut Thorat
  • 269
  • 4
  • 3
3

WeapMap in javascript does not hold any keys or values, it just manipulates key value using a unique id and define a property to the key object.

because it define property to key object by method Object.definePropert(), key must not be primitive type.

and also because WeapMap does not contain actually key value pairs, we cannot get length property of weakmap.

and also manipulated value is assigned back to the key object, garbage collector easily can collect key if it in no use.

Sample code for implementation.

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

reference of implementation

Ravi Sevta
  • 1,966
  • 14
  • 29
  • 1
    To be clear, this implementation only half works. It won't allow using the same object as key in multiple weak maps. It also does not work for frozen objects. And of course, it leaks the mapping to anybody who has a reference to the object. The first can be fixed using symbols, but not the latter two. – Andreas Rossberg Jul 12 '20 at 16:13
  • @AndreasRossberg In this implementation I have added hardcoded `id`, but this should be unique by using something Math.random and Date.now(), etc. And by adding this dynamic id, the first point can be solved. Could you please provide me a solution for the last two points. – Ravi Sevta Jul 13 '20 at 05:16
  • The first problem is solved more elegantly by using symbols. The latter two cannot be solved within JS, which is why WeakMap has to be a primitive in the language. – Andreas Rossberg Jul 13 '20 at 07:18
2

WeakMap keys must be objects, not primitive values.

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

Why????

Let's see below example.

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

If we use an object as the key in a regular Map, then while the Map exists, that object exists as well. It occupies memory and may not be garbage collected.

WeakMap is fundamentally different in this aspect. It doesn’t prevent garbage-collection of key objects.

let user = { name: "User" };

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

user = null; // overwrite the reference

// 'user' is removed from memory!

if we use an object as the key in it, and there are no other references to that object – it will be removed from memory (and from the map) automatically.

WeakMap does not support iteration and methods keys(), values(), entries(), so there’s no way to get all keys or values from it.

WeakMap has only the following methods:

  • weakMap.get(key)
  • weakMap.set(key, value)
  • weakMap.delete(key)
  • weakMap.has(key)

That is obvious as if an object has lost all other references (like 'user' in the code above), then it is to be garbage-collected automatically. But technically it’s not exactly specified when the cleanup happens.

The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or to wait and do the cleaning later when more deletions happen. So, technically the current element count of a WeakMap is not known. The engine may have cleaned it up or not or did it partially. For that reason, methods that access all keys/values are not supported.

Note:- The main area of application for WeakMap is an additional data storage. Like caching an object until that object gets garbage collected.

Pravin Divraniya
  • 3,642
  • 2
  • 26
  • 45