What is the proper usage of the WeakMap in JavaScript? What kind of timing issues may occur when I use it? IN particular, I am wondering what would happen in the following situation:
var wm1 = new WeakMap()
var o1 = {},
o2 = function(){},
o3 = window;
// in other method:
wm1.set(o1, 37);
wm1.set(o2, "azerty");
if (wm1.has(o2)) {
//Garbage collection happen here, objects from wm1 may no longer exists
Console.log(wm1.get(o2)) // what will happen here? just undefined? null?
}
how GC will affect WeakMaps?
Update: my bad, I missed the fact that you can't have string as keys in WeakMap, my question does not make if I take into account that fact.
WeakMaps are explicitly designed to not exhibit the least observable garbage collection behaviour. There will be absolutely zero issues.
In your specific situtation, as long as you hold a reference to the object or to the function (through the live variables o1 and o2 that are still on the stack), you will be able to find them in the WeakMap or WeakSet. As soon as you don't hold a reference to them any more, and nobody does, they are eligible for garbage collection (just as usual) - and given that, nobody will be able to try to look them up in the collection.
Related
According to MDN, a WeakMap object
does not create strong references to its keys, so values in any
WeakMap become candidates for garbage collection as well — as long as
they aren't strongly referred to elsewhere.
But when I try the code below in Chrome, WeakMap doesn't work as expected:
const wm = new WeakMap()
function foo(){
const key = {bar: 1};
const value = {bar: 2};
wm.set(key, value)
}
foo()
console.log(wm)
Why there's still object in the WeakMap ?
The contents of a weakmap will only change when GC (garbage collection) is called.
When testing weakmaps in a webpage, the page has to have a reason to trigger the GC for the weakmap to perform its tasks.
Going on a fairly static site with minimal elements such as github will take a while before the GC is called.
Going to a page like youtube and jumping around the video's timeline will trigger the GC much faster as there will be a lot more memory management happening in the browser.
The WeakSet is supposed to store elements by weak reference. That is, if an object is not referenced by anything else, it should be cleaned from the WeakSet.
I have written the following test:
var weakset = new WeakSet(),
numbers = [1, 2, 3];
weakset.add(numbers);
weakset.add({name: "Charlie"});
console.log(weakset);
numbers = undefined;
console.log(weakset);
Even though my [1, 2, 3] array is not referenced by anything, it's not being removed from the WeakSet. The console prints:
WeakSet {[1, 2, 3], Object {name: "Charlie"}}
WeakSet {[1, 2, 3], Object {name: "Charlie"}}
Why is that?
Plus, I have one more question. What is the point of adding objects to WeakSets directly, like this:
weakset.add({name: "Charlie"});
Are those Traceur's glitches or am I missing something?
And finally, what is the practical use of WeakSet if we cannot even iterate through it nor get the current size?
it's not being removed from the WeakSet. Why is that?
Most likely because the garbage collector has not yet run. However, you say you are using Traceur, so it just might be that they're not properly supported. I wonder how the console can show the contents of a WeakSet anyway.
What is the point of adding objects to WeakSets directly?
There is absolutely no point of adding object literals to WeakSets.
What is the practical use of WeakSet if we cannot even iterate through it nor get the current size?
All you can get is one bit of information: Is the object (or generically, value) contained in the set?
This can be useful in situations where you want to "tag" objects without actually mutating them (setting a property on them). Lots of algorithms contain some sort of "if x was already seen" condition (a JSON.stringify cycle detection might be a good example), and when you work with user-provided values the use of a Set/WeakSet would be advisable. The advantage of a WeakSet here is that its contents can be garbage-collected while your algorithm is still running, so it helps to reduce memory consumption (or even prevents leaks) when you are dealing with lots of data that is lazily (possibly even asynchronously) produced.
This is a really hard question. To be completely honest I had no idea in the context of JavaScript so I asked in esdiscuss and got a convincing answer from Domenic.
WeakSets are useful for security and validation reasons. If you want to be able to isolate a piece of JavaScript. They allow you to tag an object to indicate it belongs to a special set of object.
Let's say I have a class ApiRequest:
class ApiRequest {
constructor() {
// bring object to a consistent state, use platform code you have no direct access to
}
makeRequest() {
// do work
}
}
Now, I'm writing a JavaScript platform - my platform allows you to run JavaScript to make calls - to make those calls you need a ApiRequest - I only want you to make ApiRequests with the objects I give you so you can't bypass any constraints I have in place.
However, at the moment nothing is stopping you from doing:
ApiRequest.prototype.makeRequest.call(null, args); // make request as function
Object.create(ApiRequest.prototype).makeRequest(); // no initialization
function Foo(){}; Foo.prototype = ApiRequest.prototype; new Foo().makeRequest(); // no super
And so on, note that you can't keep a normal list or array of ApiRequest objects since that would prevent them from being garbage collected. Other than a closure, anything can be achieved with public methods like Object.getOwnPropertyNames or Object.getOwnSymbols. So you one up me and do:
const requests = new WeakSet();
class ApiRequest {
constructor() {
requests.add(this);
}
makeRequest() {
if(!request.has(this)) throw new Error("Invalid access");
// do work
}
}
Now, no matter what I do - I must hold a valid ApiRequest object to call the makeRequest method on it. This is impossible without a WeakMap/WeakSet.
So in short - WeakMaps are useful for writing platforms in JavaScript. Normally this sort of validation is done on the C++ side but adding these features will enable moving and making things in JavaScript.
(Of course, everything a WeakSet does a WeakMap that maps values to true can also do, but that's true for any map/set construct)
(Like Bergi's answer suggests, there is never a reason to add an object literal directly to a WeakMap or a WeakSet)
By definition, WeakSet has only three key functionalities
Weakly link an object into the set
Remove a link to an object from the set
Check if an object has already been linked to the set
Sounds more pretty familiar?
In some application, developers may need to implement a quick way to iterate through a series of data which is polluted by lots and lots of redundancy but you want to pick only ones which have not been processed before (unique). WeakSet could help you. See an example below:
var processedBag = new WeakSet();
var nextObject = getNext();
while (nextObject !== null){
// Check if already processed this similar object?
if (!processedBag.has(nextObject)){
// If not, process it and memorize
process(nextObject);
processedBag.add(nextObject);
}
nextObject = getNext();
}
One of the best data structure for application above is Bloom filter which is very good for a massive data size. However, you can apply the use of WeakSet for this purpose as well.
A "weak" set or map is useful when you need to keep an arbitrary collection of things but you don't want their presence in the collection from preventing those things from being garbage-collected if memory gets tight. (If garbage collection does occur, the "reaped" objects will silently disappear from the collection, so you can actually tell if they're gone.)
They are excellent, for example, for use as a look-aside cache: "have I already retrieved this record, recently?" Each time you retrieve something, put it into the map, knowing that the JavaScript garbage collector will be the one responsible for "trimming the list" for you, and that it will automatically do so in response to prevailing memory conditions (which you can't reasonably anticipate).
The only drawback is that these types are not "enumerable." You can't iterate over a list of entries – probably because this would likely "touch" those entries and so defeat the purpose. But, that's a small price to pay (and you could, if need be, "code around it").
WeakSet is a simplification of WeakMap for where your value is always going to be boolean true. It allows you to tag JavaScript objects so to only do something with them once or to maintain their state in respect to a certain process. In theory as it doesn't need to hold a value it should use a little less memory and perform slightly faster than WeakMap.
var [touch, untouch] = (() => {
var seen = new WeakSet();
return [
value => seen.has(value)) || (seen.add(value), !1),
value => !seen.has(value) || (seen.delete(value), !1)
];
})();
function convert(object) {
if(touch(object)) return;
extend(object, yunoprototype); // Made up.
};
function unconvert(object) {
if(untouch(object)) return;
del_props(object, Object.keys(yunoprototype)); // Never do this IRL.
};
Your console was probably incorrectly showing the contents due to the fact that the garbage collection did not take place yet. Therefore since the object wasn't garbage collected it would show the object still in weakset.
If you really want to see if a weakset still has a reference to a certain object then use the WeakSet.prototype.has() method. This method, as the name implies returns a boolean indicating wether the object still exists in the weakset.
Example:
var weakset = new WeakSet(),
numbers = [1, 2, 3];
weakset.add(numbers);
weakset.add({name: "Charlie"});
console.log(weakset.has(numbers));
numbers = undefined;
console.log(weakset.has(numbers));
Let me answer the first part, and try to avoid confusing you further.
The garbage collection of dereferenced objects is not observable! It would be a paradox, because you need an object reference to check if it exists in a map. But don't trust me on this, trust Kyle Simpson:
https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/es6%20%26%20beyond/ch5.md#weakmaps
The problem with a lot of explanations I see here, is that they re-reference a variable to another object, or assign it a primitive value, and then check if the WeakMap contains that object or value as a key. Of course it doesn't! It never had that object/value as a key!
So the final piece to this puzzle: why does inspecting the WeakMap in a console still show all those objects there, even after you've removed all of your references to those objects? Because the console itself keeps persistent references to those Objects, for the purpose of being able to list all the keys in the WeakMap, because that is something that the WeakMap itself cannot do.
While I'm searching about use cases of Weakset I found these points:
"The WeakSet is weak, meaning references to objects in a WeakSet are held weakly.
If no other references to an object stored in the WeakSet exist, those objects can be garbage collected."
##################################
They are black boxes: we only get any data out of a WeakSet if we have both the WeakSet and a value.
##################################
Use Cases:
1 - to avoid bugs
2 - it can be very useful in general to avoid any object to be visited/setup twice
Refrence: https://esdiscuss.org/topic/actual-weakset-use-cases
3 - The contents of a WeakSet can be garbage collected.
4 - Possibility of lowering memory utilization.
Refrence: https://www.geeksforgeeks.org/what-is-the-use-of-a-weakset-object-in-javascript/
##################################
Example on Weakset: https://exploringjs.com/impatient-js/ch_weaksets.html
I Advice you to learn more about weak concept in JS: https://blog.logrocket.com/weakmap-weakset-understanding-javascript-weak-references/
I have a JavaScript object Team and a Score which represent points and some other functions. I want to know if it's safe to store the team in the score at the same time as storing the score in the team.
var Score = function(team){
this.team = team;
this.points = 0;
...
}
var team = {
name : 'Team 1',
}
team.score = new Score(team);
The result of this is that if I log team.score.team.score.team.score.team.score.team.score.points = 0. This is perfect for what I am programming, however does it represent a dangerous setup that may crash older browsers or cause any other issues? It looks exactly like an infinite loop however Chrome seems to be handling it fine.
Are there any reasons why I shouldn't do this?
Good question by the way.
This is called circular referencing.
Meaning the you are creating the nested reference of the same object.
Garbage collection in browsers: The main function of the garbage collector in the browser is to free the memory if the memory occupied by the object is no longer in use. But in the case of circular reference
An object is said to reference another object if the former has an
access to the latter (either implicitly or explicitly). For instance,
a JavaScript object has a reference to its prototype (implicit
reference) and to its properties values (explicit reference)
(Source MDN)
This is forcing the garbage collecting algorithm to prevent the object from being garbage collected, which in turn is a memory leak.
As per the MDN Mark and sweep algorithm is been improved in such circumstance of circular referencing which is intelligent enough to remove the object of this type.
Circular referencing was a problem in IE < 8 which caused the IE browsers to go hay wire on this. Read this link and this one
IBM link
This article sheds light on JavaScript circular referencing memory leak with example and clarity on the subject.
Final Verdict: Better to avoid circular referenced objects, only use when its highly needed at programmers discretion. As modern browsers today are quite efficiently built though but its not a good practice as a developer to write code that causes unwanted memory consumption and leaks.
Diagrammatic Represation For Circular Referencing
Consider the code snippet below:
const obj = {
id: 1
};
obj.cirRef = obj;
console.log(obj.cirRef === obj); // true
console.log(obj.cirRef.cirRef === obj); // true
console.log(obj.cirRef.cirRef.cirRef.cirRef.id); // 1
Here's a diagrammatic representation for the same:
Now using the diagram above, follow the wires and try to answer what this expression obj.cirRef.cirRef.id evaluates to, the answer is 1.
var Score = function(team,point){
this.team = team;
this.points = 0;
...
}
var team = {
name : 'Team 1',
point : 'point'
}
team.score = new Score(team);
team.score = new Score(point);
Try this, maybe it can help you
var SomeObj = function() {
this.i = 0;
};
setTimeout(function() {
new SomeObj; // I mean this object
}, 0);
At what point is the SomeObj object garbage collected?
It is eligible for garbage collection as soon as it is no longer used.
That means immediately after the constructor call in your case.
How timely this actually happens is an implementation detail. If you run into GC issues, you need to dig into your specific Javascript engine.
An object that is not referenced from anywhere doesn't "exist" at all from the view of your program. How long it still resides somewhere in memory depends on the garbage collection characteristics of your interpreter, and when/whether it feels the need to collect it.
In your specific case, the object does become eligible for garbage collection right after it has been created and the reference that the expression yields is not used (e.g. in an assignment). In fact, the object might not get created at all in the first place, an optimising compiler could easily remove the whole function altogether - it has no side effects and no return value.
Two jsperfs relating to the question:
Cache-ing 'this'
Cache-ing booleans
I'm on Mac 10.9. In Safari 7, Chrome 32, and Firefox 26, storing 'this' inside a variable seems to run slightly slower than not storing it. For example:
function O() {
var THIS = this;
THIS.foo = 'foo';
THIS.bar = 'bar';
THIS.baz = 'baz';
}
was a bit slower than:
function O() {
this.foo = 'foo';
this.bar = 'bar';
this.baz = 'baz';
}
Why is this? Is it because 'this' references the original object every time?
In Chrome and Firefox, storing a Boolean object and then referencing the value of that variable later seemed to run a bit faster than writing 'true' or 'false' every time (in theory, creating a new Boolean object every time.) BUT, in Safari, the opposite appeared to be true. For example:
function lt() {
if(arguments[0] < arguments[1]) return true;
return false;
}
was a bit faster (in Firefox and Chrome) than:
var TRUE = true,
FALSE = false;
function lt() {
if(arguments[0] < arguments[1]) return TRUE;
return FALSE;
}
With the exception of Safari, is this because a new Boolean object is being created every time when not storing it inside a variable? What could be the explanation as to why there was the opposite effect in Safari?
I'm inclined to think that in small bits of code, the difference would be negligible, but I'm curious if it could make a difference with which someone should be concerned when the code gets much lengthier. I also read a question asking about performance data vs. perceived performance, where perceived performance is generally the thing to look at in these cases.
An issue with the above statistics in the jsperfs is the lack of a large data sample. The reason I ask this question is because I'm writing a small JS library. In that context, what are best practices as far as 'caching' certain objects?
It's hard to answer your question as to why safari behaves differently from Firefox and Chrome in these examples, because the reason is very implementation-dependent and I'm not familiar with the source codes of either of these browsers; nor have I spent a long time trying to reverse-engineer them. But I can give you a rough sketch of how variable caching influences performance just using the ECMAScript spec:
First it's important to understand that the this keyword is initialized during invocation, and it's always local to your execution context, i.e. the look-up for the this keyword will terminate on the activation record of the current function invocation. So in your first example you're creating an additional local variable - double the work - leaving the execution context with following activation record (omitted other system-defined properties, e.g. arguments):
{ this: caller, THIS: caller };
// this: system-created property (?during function object creation or invocation?)
// caller: system-initialized value, during invocation
// THIS: user-created-and-initialized property, slows down execution
So in general the best reason to cache a variable is because the look-up operation is more expensive than creating a local property. Creating a local property obviously only benefits performance if it is referenced more than one time. Fore mentioned operation can be done on the scope-chain, or on the prototype-chain using the dotted look-up operation. As long as property creation is less expensive than the actual look-up operation, you can cache the property.
The best reason to cache a value, is to avoid creating the same values over and over for every invocation. The best way to cache a value is through an (anonymous) closure. E.g.:
var obj0, obj1, funExt;
obj0 = {};
obj1 = {};
funExt = (function () {
var cachedId, cachedObj;
// After evaluation of funExt is done these values will persist in closure,
// avoiding value creation during the execution of returned function.
cachedId = "extension";
cachedObj = { prop0: myValue, prop1: myValue, prop2: myValue };
return function (o) {
if (o) {
o[cachedId] = cachedObject;
} else if (this !== window) {
o = (this[cachedId] = cachedObj);
}
return o;
};
}());
obj0.funExt();
funExt(obj1);
Concerning your second example I can only say that in Chrome and Firefox boolean creation is less expensive than a one-level scope look-up. Given the complexity of the example function both operations should be rather cheap.
It's also good to keep in mind that developers design javascript engines to do these in-line optimizations on the fly. So there is no guarantee that these optimizations will yield significant performance boosts. I prefer to stick to simplicity and clarity of structure when building libraries. So I use closure to either communicate that values are being shared, or that certain values remain constant. And I use variable caching if the given variable is too long or lacks clarity.
In general, the more deeply an object is in your object graph, the more reason to cache it. So for example: no reason to cache this in your example, but if you have an object foo.bar.something.else.x and want to use multiple times, then chances are you better off caching that into a local variable. It will be faster, and also more readable.
Another good reason to cache an object if it's multiple levels up in your scope chain. For example, when you have 4 levels of nested functions and in the innermost you want to use a variable from the global scope multiple times, it's reasonable to cache it in a local variable.