23

I've run across a JavaScript library that implement a cross-browser WeakMap in ES5. (WeakMap is slated for ES6.)

How can this possibly work without support in the JavaScript language itself?

Edit: Just to be clear, I'm referring to a Weak Map, not a regular Map. I tested this project out using Chrome's profiler and the keys are not held by strong references. They get GC'ed without having to remove them from the WeakMap.

paleozogt
  • 6,393
  • 11
  • 51
  • 94
  • 3
    Consider studying the source code. –  May 03 '13 at 19:17
  • 4
    @squint It's doing something fairly deep-- I can't figure out how its not holding a strong reference to the keys. It's not using Arrays, for example. – paleozogt May 03 '13 at 21:08
  • 5
    WeakMaps are an ES6 feature that allows you to associate data with an object, but still let that data be garbage collected when either the object -OR- the WeakMap instance itself is garbage collected. It's impossible to do both of these without language support. Most WeakMap shims ignore the part about letting the data be GC'd when the WeakMap instance itself is GC'd. – Macil Dec 22 '14 at 23:23

1 Answers1

37

It took me a while to grok the code, but then it hit me: the key itself is used to store a reference to the value.

For example, several layers into set it does

defProp(obj, globalID, { value: store });

where defProp has been defined to be Object.defineProperty, obj is the key, globalID is a guid and store is a storage object that contains the value.

Then down in get it looks up the value with

obj[globalID];

This is very clever. The WeakMap doesn't actually contain a reference to anything (weak or otherwise)-- it just sets up a policy of where to secretly store the value. The use of Object.defineProperty means that you won't accidentally discover the value storage-- you have to know the magic guid to look it up.

Since the key directly refers to the value (and the WeakMap doesn't refer to it), when all references to the key are gone, it gets GCed like normal.

paleozogt
  • 6,393
  • 11
  • 51
  • 94
  • If `obj` is the WeakMap key, and `globalID` is defined on `obj`, I wonder why the `globalID` doesn't show up when using `Object.getOwnPropertyNames()`. –  May 03 '13 at 21:58
  • 2
    i was wondering the same; too bad the lib is so far up it's own behind as to be virtually unreadable. there has to be a simpler explanation... – dandavis May 03 '13 at 23:33
  • 1
    figured it out: the lib cheats: it re-defines Object.getOwnPropertyNames(). boo for stepping on existing native functions. – dandavis May 03 '13 at 23:49
  • @dandavis haha i agree about its readability, which is why i posted this question in the first place :) – paleozogt May 04 '13 at 17:07
  • 6
    It only redefines `getOwnPropertyNames` in order to pull off a WeakMap. It doesn't break anything, and `getOwnPropertyNames`' functionality is left in-tact. The environment is left fully backwards compatible with ES5 and fully forwards compatible with ES6 WeakMaps. It's a really great idea. I've used it to the same effect in [Secrets](http://github.com/Nathan-Wall/Secrets). – Nathan Wall May 07 '13 at 13:38
  • 4
    The shim does not provide one of the main properties of a WeakMap: That the value is held weakly. This shim creates a strong reference between the key and the value ... similar to just setting the value as a property on the key object. – Stef Nov 26 '14 at 09:40
  • I'm getting mixed messages here, the answer indicates that it does adequately simulate weakmap and allows its keys and values to be GCed when there's no more reference to the key object. But the @Stef comment says that it lacks one the "main properties". Is this true? Is there a way to empirically test if the objects are really garbage collected? – CMCDragonkai Sep 16 '17 at 07:22
  • My understanding of a weakmap is that it just adds a kind of secret/private property to the object, so it actually contains nothing, it simply mediates this acquisition of the secret property of the object. Thus it allows the key objects to be GCed. – CMCDragonkai Sep 16 '17 at 08:32
  • I'm pretty sure it doesn't work. obj[globalId] is itself a strong reference, so it's going to leak. Unless you have a way of retrieving globalId from the object itself, you can't use the "weakmap" to retrieve the stored value, which defeats its purpose. WeakMap cannot be accomplished without direct access to GC, so any shim will leak. – podperson Feb 25 '21 at 23:05
  • @Stef A reference from the key to the value is normal, that's what you would expect. If the key is no longer referenced by anything and gets garbage-collected, the value is no longer referenced from the "map" either. However, what's missing is that the reference should be removed also when the weak map itself gets garbage-collected. – Bergi Jul 22 '22 at 15:22