What are ECMAScript 6 WeakMaps? - javascript

After reading this description: http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps
I'm trying to get a hang of it, but I do not get the overall picture. What is it all about? It seems to be supported in Firefox 6: http://kangax.github.com/es5-compat-table/non-standard/

A weak reference is a special object containing an object-pointer, but does not keep that object alive.
One application of weak references are implemented in Weak Maps:
“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.” Source
(See also my post when ECMAScript Harmony was first released with Firefox... )

WeakMap
WeakMaps basically allow you to have a HashTable with a key that isn't a String.
So you can set the key to be, i.e. [1] and then can say Map.get([1])
Example from the MDN:
var wm1 = new WeakMap(),
wm2 = new WeakMap();
var o1 = {},
o2 = function(){},
o3 = window;
wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // a value can be anything, including an object or a function
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps!
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined, because there is no value for o2 on wm2
wm2.get(o3); // undefined, because that is the set value
wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (even if the value itself is 'undefined')
wm1.has(o1); // true
wm1.delete(o1);
wm1.has(o1); // false
The reason for its existance is:
in order to fix a memory leak present in many uses of weak-key tables.
Apparently emulating weakmaps causes memory leaks. I don't know the details of those memory leaks.

WeakMap allows to use objects as keys.
It does not have any method to know the length of the map. The length is always 1.
The key can't be primitive values
A word of caution about using object as key is, since all the objects are by default singletons in JavaScript we should be creating an object reference and use it.
This is because when we create anonymous objects they are different.
if ( {} !== {} ) { console.log('Objects are singletons') };
// will print "Objects are singletons"
So in the following scenario, we can't expect to get the value
var wm = new WeakMap()
wm.set([1],'testVal');
wm.get([1]); // will be undefined
And the following snippet will work as expected.
var a = [1];
wm.set(a, 'testVal');
wm.get(a); // will return 'testVal'

Related

what is the diffrence betwean deepFreeze and true imutibility

Hello and thank you in advanced.
I recently read about object.freez and deep.freez
And considering that js has no immutable structures, I'm now left wondering how this differs from the standert term of immutability...
"quote: As said before JavaScript has no immutable structures, but immutability can be achieved by principles and rules."
how ever seeing: code from :source
function deepFreeze(object) {
// Retrieve the property names defined on object
var propNames = Object.getOwnPropertyNames(object);
// Freeze properties before freezing self
for (let name of propNames) {
let value = object[name];
object[name] = value && typeof value === "object" ?
deepFreeze(value) : value;
}
return Object.freeze(object);
}
var obj2 = {
internal: {
a: null
}
};
deepFreeze(obj2);
obj2.internal.a = 'anotherValue'; // fails silently in non-strict mode
obj2.internal.a; // null
I'm now left at a bit perplexed
I thought immutability meant not being able to mutate(change) the values of an object after it has bean created. And as far as i can tell deep.freez achieves exactly that... So what is the difference?
hope this question made sense as i was not able to find any information concerning deep.freez
Kind regards Lost.
Both points are correct - there are no data structures in JavaScript that are immutable by default. For example, there is no data structure you can create which is an "immutable object" at the time of creation.
However, Object.freeze is a method in the JavaScript/ECMAScript specification which freezes the properties of an object after it's been created, essentially making it immutable.
Your deepFreeze function, at its heart, calls Object.freeze. deepFreeze ensures that if an object has a property which is also an object, it will also be frozen.
The article you linked to actually mentions Object.freeze as a method of creating "immutable" objects.

How do Maps work in Ecmascript 6 with Object keys?

I have been examining the spec for the upcoming Map data structure in ES6. It's supposed to be great because anything can be a key in a Map, not just strings, but when I tried it with a few object examples, I could not retrieve the values I had inserted into the Map.
var _projects = new Map();
_projects.set({}, [...]);
_projects.set({page: 2}, [...]);
_projects.has({page:2})
false
Then I saw this in the Mozilla developers site:
Key equality is based on the "same-value" algorithm: NaN is considered
the same as NaN (even though NaN !== NaN) and all other values are
considered equal according to the semantics of the === operator.
If this is the case, then what are my options if I want to retrieve the values for an object based on its value, rather than its identity?
Remember that {x:5} !== {x:5} is true because they are two different objects.
You have to reference them.
Meaning you can't just _projects.has({x:5}) because that's a new object.
You have to do the following:
var _projects = new Map();
var obj = {x:5};
_projects.set(obj, [1,2]);
_projects.has(obj) //true;

Delete objects with delete or by assigning null in Javascript?

How shall I delete objects in Javascript to properly destroy them (call destructors if there are any?) and prevent memory leaks? I've seen two ways:
delete obj;
and
obj = null;
But even after reading this I have not understood what is really the difference and what should I use.
Also, I guess there are not real destructors in Javascript, but in case of complex relationship of multiple objects is it really enough to rely on garbage collector to prevent memory leaks?
One major difference between the two is the value of obj after the operation. Consider
x = 42
x = null
console.log(x) // prints: null
delete x;
console.log(x) // prints: undefined
Assigning null is giving a new value to an existing property. It will still exist after the operation. When you delete a property it is removed entirely.
Google has something to say about delete:
Prefer this.foo = null
Foo.prototype.dispose = function() {
this.property_ = null;
};
Instead of:
Foo.prototype.dispose = function() {
delete this.property_;
};
In modern JavaScript engines, changing the number of properties on an
object is much slower than reassigning the values. The delete keyword
should be avoided except when it is necessary to remove a property
from an object's iterated list of keys, or to change the result of if
(key in obj).
Edit
Some performance test: http://jsperf.com/delete-vs-nullify
The article you have linked says that delete only deletes a reference, therefore there is no difference between the two really performance wise. The only theoretical difference is the garbage collector will be more efficient because it's been explicitly told what to do, but unless you have incredibly large complex objects with many references in hard to reach locations it shouldn't be an issue.
The other answer on the question explains an actual usecase that highlights a difference, ie. removing a property from an object. Accessing it would then give you undefined rather than null.
Here's an example of the latter: http://jsfiddle.net/YPSqM/
function cat() { this.name = 'mittens'; };
var myCat = new cat();
alert(myCat.name); // 'mittens'
myCat.name = null;
alert(myCat.name === null); // 'true' as it is null but not undefined
delete myCat.name;
alert(myCat.name === undefined); // 'true' as it is undefined (but not null)

Detecting if a Property of an Object is an Object

I'm going to use JSON to stringify the value of a property of an object in order to store it as a Tag on Google Calendar through Google Apps Script. The value is actually a double-nested Object (see extraAttributes in myObject).
I have an Object that is built like the following:
var myObject = {
"location": "somwehere",
"date": new Date(),
"numSomething": 20,
"extraAttributes": {"used": true, "interval": 60, "internals": {"timer": 10, "visible": false} }
}
I tried to make it pretty and readable... anyway: myObject actually has anywhere from 20-40 properties, some of which are nested Objects. So here's the question:
Is there a way to notify if the value of a property in an Object is an object itself (would extra levels of nesting be an issue for this detection?)? The reasoning behind this is that I don't know how JSON.stringify and JSON.parse will affect the other data (I've tested it on that one particular value of type Object and it worked fine), and also that I don't know how much of a performance impact that those two functions would have on my script if the properties being stored reach 20-40.
I would rather do a check to see if the value is an Object and stringify only that (would that also be inefficient?). Feel free to lecture me on Objects and nesting if this would cause major problems in the future ;)
All Javascript values, except functions and undefined, can be serialized as JSON.
To answer your question, see the typeof operator.
Another way to check if something is an object instead of using typeof (which can be misleading in some cases — for example typeof [] === 'object') is to use the Object constructor:
var foo = { id: 1 };
var bar = 'string';
console.log(foo === Object(foo)) // true
console.log(bar === Object(bar)) // false

Cloning: what's the fastest alternative to JSON.parse(JSON.stringify(x))?

What's the fastest alternative to
JSON.parse(JSON.stringify(x))
There must be a nicer/built-in way to perform a deep clone on objects/arrays, but I haven't found it yet.
Any ideas?
No, there is no build in way to deep clone objects.
And deep cloning is a difficult and edgey thing to deal with.
Lets assume that a method deepClone(a) should return a "deep clone" of b.
Now a "deep clone" is an object with the same [[Prototype]] and having all the own properties cloned over.
For each clone property that is cloned over, if that has own properties that can be cloned over then do so, recursively.
Of course were keeping the meta data attached to properties like [[Writable]] and [[Enumerable]] in-tact. And we will just return the thing if it's not an object.
var deepClone = function (obj) {
try {
var names = Object.getOwnPropertyNames(obj);
} catch (e) {
if (e.message.indexOf("not an object") > -1) {
// is not object
return obj;
}
}
var proto = Object.getPrototypeOf(obj);
var clone = Object.create(proto);
names.forEach(function (name) {
var pd = Object.getOwnPropertyDescriptor(obj, name);
if (pd.value) {
pd.value = deepClone(pd.value);
}
Object.defineProperty(clone, name, pd);
});
return clone;
};
This will fail for a lot of edge cases.
Live Example
As you can see you can't deep clone objects generally without breaking their special properties (like .length in array). To fix that you have to treat Array seperately, and then treat every special object seperately.
What do you expect to happen when you do deepClone(document.getElementById("foobar")) ?
As an aside, shallow clones are easy.
Object.getOwnPropertyDescriptors = function (obj) {
var ret = {};
Object.getOwnPropertyNames(obj).forEach(function (name) {
ret[name] = Object.getOwnPropertyDescriptor(obj, name);
});
return ret;
};
var shallowClone = function (obj) {
return Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
};
I was actually comparing it against angular.copy
You can run the JSperf test here:
https://jsperf.com/angular-copy-vs-json-parse-string
I'm comparing:
myCopy = angular.copy(MyObject);
vs
myCopy = JSON.parse(JSON.stringify(MyObject));
This is the fatest of all test I could run on all my computers
The 2022 solution for this is to use structuredClone
See : https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
structuredClone(x)
Cyclic references are not really an issue. I mean they are but that's just a matter of proper record keeping. Anyway quick answer for this one. Check this:
https://github.com/greatfoundry/json-fu
In my mad scientist lab of crazy javascript hackery I've been putting the basic implementation to use in serializing the entirety of the javascript context including the entire DOM from Chromium, sending it over a websocket to Node and reserializing it successfully. The only cyclic issue that is problematic is the retardo navigator.mimeTypes and navigator.plugins circle jerking one another to infinity, but easily solved.
(function(mimeTypes, plugins){
delete navigator.mimeTypes;
delete navigator.plugins;
var theENTIREwindowANDdom = jsonfu.serialize(window);
WebsocketForStealingEverything.send(theENTIREwindowANDdom);
navigator.mimeTypes = mimeTypes;
navigator.plugins = plugins;
})(navigator.mimeTypes, navigator.plugins);
JSONFu uses the tactic of creating Sigils which represent more complex data types. Like a MoreSigil which say that the item is abbreviated and there's X levels deeper which can be requested. It's important to understand that if you're serializing EVERYTHING then it's obviously more complicated to revive it back to its original state. I've been experimenting with various things to see what's possible, what's reasonable, and ultimately what's ideal. For me the goal is a bit more auspicious than most needs in that I'm trying to get as close to merging two disparate and simultaneous javascript contexts into a reasonable approximation of a single context. Or to determine what the best compromise is in terms of exposing the desired capabilities while not causing performance issues. When you start looking to have revivers for functions then you cross the land from data serialization into remote procedure calling.
A neat hacky function I cooked up along the way classifies all the properties on an object you pass to it into specific categories. The purpose for creating it was to be able to pass a window object in Chrome and have it spit out the properties organized by what's required to serialize and then revive them in a remote context. Also to accomplish this without any sort of preset cheatsheet lists, like a completely dumb checker that makes the determinations by prodding the passed value with a stick. This was only designed and ever checked in Chrome and is very much not production code, but it's a cool specimen.
// categorizeEverything takes any object and will sort its properties into high level categories
// based on it's profile in terms of what it can in JavaScript land. It accomplishes this task with a bafflingly
// small amount of actual code by being extraordinarily uncareful, forcing errors, and generally just
// throwing caution to the wind. But it does a really good job (in the one browser I made it for, Chrome,
// and mostly works in webkit, and could work in Firefox with a modicum of effort)
//
// This will work on any object but its primarily useful for sorting the shitstorm that
// is the webkit global context into something sane.
function categorizeEverything(container){
var types = {
// DOMPrototypes are functions that get angry when you dare call them because IDL is dumb.
// There's a few DOM protos that actually have useful constructors and there currently is no check.
// They all end up under Class which isn't a bad place for them depending on your goals.
// [Audio, Image, Option] are the only actual HTML DOM prototypes that sneak by.
DOMPrototypes: {},
// Plain object isn't callable, Object is its [[proto]]
PlainObjects: {},
// Classes have a constructor
Classes: {},
// Methods don't have a "prototype" property and their [[proto]] is named "Empty"
Methods: {},
// Natives also have "Empty" as their [[proto]]. This list has the big boys:
// the various Error constructors, Object, Array, Function, Date, Number, String, etc.
Natives: {},
// Primitives are instances of String, Number, and Boolean plus bonus friends null, undefined, NaN, Infinity
Primitives: {}
};
var str = ({}).toString;
function __class__(obj){ return str.call(obj).slice(8,-1); }
Object.getOwnPropertyNames(container).forEach(function(prop){
var XX = container[prop],
xClass = __class__(XX);
// dumping the various references to window up front and also undefineds for laziness
if(xClass == "Undefined" || xClass == "global") return;
// Easy way to rustle out primitives right off the bat,
// forcing errors for fun and profit.
try {
Object.keys(XX);
} catch(e) {
if(e.type == "obj_ctor_property_non_object")
return types.Primitives[prop] = XX;
}
// I'm making a LOT flagrant assumptions here but process of elimination is key.
var isCtor = "prototype" in XX;
var proto = Object.getPrototypeOf(XX);
// All Natives also fit the Class category, but they have a special place in our heart.
if(isCtor && proto.name == "Empty" ||
XX.name == "ArrayBuffer" ||
XX.name == "DataView" ||
"BYTES_PER_ELEMENT" in XX) {
return types.Natives[prop] = XX;
}
if(xClass == "Function"){
try {
// Calling every single function in the global context without a care in the world?
// There's no way this can end badly.
// TODO: do this nonsense in an iframe or something
XX();
} catch(e){
// Magical functions which you can never call. That's useful.
if(e.message == "Illegal constructor"){
return types.DOMPrototypes[prop] = XX;
}
}
// By process of elimination only regular functions can still be hanging out
if(!isCtor) {
return types.Methods[prop] = XX;
}
}
// Only left with full fledged objects now. Invokability (constructor) splits this group in half
return (isCtor ? types.Classes : types.PlainObjects)[prop] = XX;
// JSON, Math, document, and other stuff gets classified as plain objects
// but they all seem correct going by what their actual profiles and functionality
});
return types;
};

Categories

Resources