Today I ran into a very odd problem using Javascript’s prototype and the this reference.
Basically, I have two objects. One object makes use of a div element and makes is possible to register an onclick event on the div. The other object makes use of this functionality and registers the onclick event using the first object.
Here is the code:
My first object, which takes care of the div:
DivObject = function() {};
DivObject.prototype.setClickListener = function(clickListener){
document.getElementById("myDiv").onclick = function(e){
clickListener(e);
};
};
My second object uses this functionality:
MainObject = function(){
this.myString = "Teststring";
this.divObj = new DivObject();
this.divObj.setClickListener(this.handleClick);
};
MainObject.prototype.handleClick = function(e){
// do something with e
};
The problem is, that inside MainObject.prototype.handleClick, the this reference refers to the window object, not to the MainObject. So, console.log(this) inside this function logs [Object window].
You can see this behaviour on jsfiddle.
As a workaround, I use the setClickListener function as follows:
var thisRef = this;
this.divObj.setClickListener(function(e){
thisRef.handleClick(e);
});
Now, the this reference in my handleClick function refers to my MainObject (as I want it to be).
See this workaround on jsfiddle.
My questions are:
Why does the this reference one time refer to the window object and one time to the this reference of my object? Is it overridden somewhere? I always thought using this in my object I can be sure that it is really the this reference of my object? Is the workaround I am using now the way how this problem should be solved or are there any other, better way to handle this case?
Your questions:
Other than using bind, the value of a function’s
thisis set by how the function is called. When you do:you are assigning a function reference, so the function is called later without any qualification (i.e. it’s called as just handleClick rather than this.handleClick). On entering the function, because its
thisisn’t set by the call, it will default to the global (window) object, or in strict mode remain undefined.No, the value of
thisis set on entering an execution context. You can’t overwrite it or directly assign to it, you can only set it in the call (e.g. as a method of an object, using new, apply, call) or using bind (also, arrow functions adopt the this of their enclosing lexical execution context).At the point you make the assignment,
thisis what you expect. But you are assigning a reference to a funciotn, not calling the function, so itsthisisn’t set at that moment but later when it’s called.Your work around is fine (and a common fix), it creates a closure so may have minor memory consequences but nothing serious. For very old versions of IE it would create a memory leak due to the circular reference involving a DOM object, but that’s fixed.
The bind solution is probably better from a clarity and perhaps maintenance viewpoint. Remember to include a "monkey patch" for browsers that don’t have built–in support for bind.
Please post code on SO, there is no guarantee that code posted elsewhere will continue to be accessible. The work around code: