Javascript objects: And what is this?
Summary
When writing object-oriented Javascript, there are two occasions when you need to be careful that this is set correctly: In inner functions and in callbacks.
this in inner functions
- If you are not in an object, this refers to the global window object.
- If you are in an object’s method, this refers to that object,
- except in an inner function, when this refers to the global window object again.
Number 3 is what you need to watch for. It is considered a bug in Javascript. Here is an illustration of the three cases:
// You need Firebug to run this script, otherwise
// replace console.log with alert.
console.log('Globally: '+ this);
var MyObj = function() { // Constructor
}
MyObj.prototype = {
test:
function() {
function inner() {
console.log("In an inner function: "+ this);
}
console.log("In an object's method: "+ this);
inner();
}
}
var m = new MyObj();
m.test();
The Firebug console will display this:
Globally: [object Window]
In an object's method: [object Object]
In an inner function: [object Window]
To get inner functions to see your object, you need to copy this into a local variable. Thanks to closures inner functions can see all the local variables of the containing function.
var MyObj = function() { // Constructor
}
MyObj.prototype = {
test:
function() {
function inner() {
// Using _this instead of this
console.log("In an inner function: "+ _this);
}
// Save in local variable
var _this = this;
console.log("In an object's method: "+ this);
inner();
}
}
var m = new MyObj();
m.test();
This will display what you expect:
In an object's method: [object Object]
In an inner function: [object Object]
this in object callbacks
Object scope is not preserved when you pass one of your object’s methods as a callback. Example:
var MyObj = function() {
}
MyObj.prototype = {
test:
function(){
console.log('test: '+ this);
setTimeout(this.onEvent, 1);
},
onEvent:
function() {
console.log('onEvent: '+ this);
}
}
var m = new MyObj();
m.test();
This prints:
test: [object Object]
onEvent: [object Window]
This is the case because when you pass this.onEvent as a callback, you are passing a function, like you might pass a string or an integer. The setTimeout function doesn’t know that it belongs to an object.
The answer is again to use the closure (that’s often the answer in Javascript), and wrap your call in an inner function, remembering the previous point about inner functions.
var MyObj = function() {
}
MyObj.prototype = {
test:
function(){
console.log('test: '+ this);
// save locally
var _this = this;
// wrap and call
setTimeout(function() {_this.onEvent()}, 1);
},
onEvent:
function() {
console.log('onEvent: '+ this);
}
}
var m = new MyObj();
m.test();
As expected, this prints:
test: [object Object]
onEvent: [object Object]
We save this in a local variable, then pass an inner function as the callback. That function has no context, but it does have a copy of our object saved in it’s _this variable. It calls onEvent on our object.
Happy Javascripting!