Reputation: 11185
A JavaScript WeakMap
does not allow you to get the key, or the length or size, by design.
Is it possible to nevertheless loop over entries in some way ?
If not .. how does the Chrome console do this ?
Upvotes: 38
Views: 21153
Reputation: 526
I needed an iterable map of objects with arrays as keys, so I came to this pattern which satisfied my requirements, sharing here incase it helps others looking into the topic:
class CustomWeakMap {
weakMap;
keys;
constructor() {
this.weakMap = new WeakMap();
this.keys = new Set();
}
clear() {
this.weakMap = new WeakMap();
this.keys = new Set();
}
delete(key) {
this.keys.remove(key);
return this.weakMap.delete(key);
}
get(key) {
return this.weakMap.get(key);
}
has(key) {
return this.keys.has(key);
}
set(k, v) {
this.keys.add(k);
this.weakMap.set(k, v);
return this;
}
forEach(callback) {
this.keys.forEach((key, index) => callback({ key, value: this.weakMap.get(key) }, index));
}
}
const x = new CustomWeakMap();
x.set([1, 'one'], 'xyz');
x.set({ why: 'not' }, [0, 0]);
x.forEach(({ key, value }) => console.log({ key, value }));
It's still certainly worth mentioning that all this functionality exists in regular Map, so if there's not some other restriction boxing you into WeakMap it's worth a look: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
Upvotes: -1
Reputation: 156
Running far afield with a qualifier in your (2015) question, specifically:
Is it possible to nevertheless loop over entries in some way ?
Yes.
In one ridiculous situation, it is possible to emulate and then iterate the keys and values of a WeakMap, and also to make a proper, independent copy of your WeakMap.
If the WeakMap you wish to clone was built in a very specific fashion by a constructor function, you can do it:
// Define a Constructor-Function
// that makes objects
// containing WeakMaps:
function makeWeakMapObject(){
this.wm1 = new WeakMap();
this.o1 = {};
this.o2 = {"orange":"orange"};
this.wm1.set(this.o1, 37);
this.wm1.set(this.o2, 'azerty');
}
// Construct a new object:
let constructedWeakMapObject = new makeWeakMapObject();
// Then set a new key-value pair
// on the WeakMap in your object;
// because, ya know, otherwise you'd
// just reuse the WeakMap constructor
// and wouldn't need to clone :D
constructedWeakMapObject.added = {"ya":"glad"};
constructedWeakMapObject.wm1.set(constructedWeakMapObject.added, 42);
// In preparation to clone your newly constructed object,
// get that newly constructed object's property descriptors:
let props = Object.getOwnPropertyDescriptors(constructedWeakMapObject);
// Have a gander at those props; just for fun:
console.log({"props":props});
// Attempt to clone the constructedWeakMapObject
// using its ownPropertyDescriptors
let weakClone = new cloneWeak(props);
// and then check out what you made:
console.log({"weakClone":weakClone});
// Verify that you've made an independent clone
// (even though this example is shallow)
// by altering the WeakMap in your weakClone:
weakClone.wm.delete(weakClone.o1);
// Make sure your clone was altered:
console.log(weakClone.wm.get(weakClone.o1));
// And then check to see that the
// changes to your clone
// don't appear on your constructed object:
console.log(constructedWeakMapObject);
console.log(constructedWeakMapObject.wm1.get(constructedWeakMapObject.o1));
// A support function to help you use fresh keys in your cloned WeakMap to keep it independent from your original WeakMap
function cloneObject(obj) { // use something more robust, like underscore: _.cloneDeep(obj); actually, you'll likely have to roll your own so you can make clones of functions... anywho
var clone = {};
for(var i in obj) {
if(typeof(obj[i])==="object" && obj[i] !== null)
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
// Called as a constructor function w/arguments
function cloneWeak(inco){ // a bit wonky, at least in the middle
this.wm = new WeakMap();
let tempMap;
for(key in inco){
// Build keys on 'this' that match the incoming keys
if(Object.prototype.toString.call(inco[key].value) !== "[object WeakMap]"){
this[key] = cloneObject(inco[key].value);
}
// Reference the incoming map from your temp map
// (this makes the following loop possible)
else{tempMap = inco[key].value;}
}
this.fakeForHack = {}; // no idea why this works
this.wm.set(this.fakeForHack, "ok"); // no idea why this works... without it, the WeakMap entry for made.wm1.get(made.added) won't transfer -> ???
for(key in inco){
if(Object.prototype.toString.call(inco[key].value) !== "[object WeakMap]"){
// Set values for 'this' WeakMap:
this.wm.set(this[key], tempMap.get(inco[key].value));
}
}
}
It's kinda ugly, it's brittle, and it only solves a ridiculous edge-case; you're welcome!
Upvotes: -1
Reputation: 28508
Things are moving and it will soon be possible to create iterable week maps thanks to weak refs. See the iterable WeakMap example in the tc39 weakrefs proposal.
(note that it is already possible with nodejs v12.?.? using --harmony-weak-refs
flag)
Upvotes: 7
Reputation: 665080
Is it possible to nevertheless loop over entries in some way?
No, as you say, the contents of a WeakMap
are not accessible by design, and there is no iterability.
If not … how does the Chrome console do this?
The console uses the debugging API of the JS engine, which allows access to the internals of objects (also to promise states, wrapped primitives, etc.) and many more.
Upvotes: 45