Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Raynos
Forked from Gozala/weak-map.js
Last active September 18, 2019 07:49
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Raynos/1638059 to your computer and use it in GitHub Desktop.
Save Raynos/1638059 to your computer and use it in GitHub Desktop.
Harmony WeakMap shim for ES5
// Original - @Gozola. This is a reimplemented version (with a few bug fixes).
window.WeakMap = window.WeakMap || (function () {
var privates = Name()
return {
get: function (key, fallback) {
var store = privates(key)
return store.hasOwnProperty("value") ?
store.value : fallback
},
set: function (key, value) {
privates(key).value = value
},
has: function(key) {
return "value" in privates(key)
},
"delete": function (key) {
return delete privates(key).value
}
}
function namespace(obj, key) {
var store = { identity: key },
valueOf = obj.valueOf
Object.defineProperty(obj, "valueOf", {
value: function (value) {
return value !== key ?
valueOf.apply(this, arguments) : store
},
writable: true
})
return store
}
function Name() {
var key = {}
return function (obj) {
var store = obj.valueOf(key)
return store && store.identity === key ?
store : namespace(obj, key)
}
}
}())
@Krinkle
Copy link

Krinkle commented Oct 10, 2012

        get: function (key, fallback) {
            return privates(key).value || fallback
        }

This fails in case of false-y values. As documented values can be anything, including (but not limited to) objects, functionss, and undefined.

var foo = {};
wm.set(foo, false);
wm.set(foo, null);
wm.set(foo, undefined);
wm.set(foo, 0);
wm.set(foo, '');

// For all of these, the below returns the default value instead of the stored value:
wm.get(foo, 'default for unset properties');

Fix:

        get: function (key, fallback) {
            var store = privates(key);
            return store.hasOwnProperty('value') ? store.value : fallback;
        }

@FritsvanCampen
Copy link

There is a (minor) bug in this implementation. It is possible to craft a value that will return a value for wm.get even though it's not in the WeakMap. A value that looks like this:

crafted_value = {
    "valueOf": function () {
        return { "value": "some value" };
    }
};

wm.get(crafted_value, "some other value"); will return "some value" instead.

You can fix this in many different ways but they all boil down to the same thing: the value that you get from the store must be associated with the identity of the WeakMap.

My solution:

Construct your store like this: (line 21)

var store = {
    identity: key
}

And check like this: (line 38)

return store && store.identity === key ? store : namespace(obj, key)

You need the truethy check on store to avoid bugs with 'strange' implementations of valueOf that return undefined. store !== undefined also works and might be better.

@Raynos
Copy link
Author

Raynos commented Mar 24, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment