Understanding "this" in JavaScript
Determining the "this" value in JavaScript function in seems to be a constant source of grief for JavaScript developers, so I feel like throwing my hat in the ring with my own attempt at explaining how it works.
It's easier than you think
As it turns out, this
, in JavaScript follows a few very simple rules and is quite easy to understand. It just so happens that a) it works very differently than C++, Java, C#, etc and b) many popular libraries, such as jQuery and backbone.js, often manage the this
value to make it behave more like the aforementioned languages. These two things together make the behavior of this
in JavaScript rather non-obvious. So let's dive in to how this
works in pure JavaScript.
First, let's look at plain old functions. Calling a function in JavaScript is simple: the this
value is always set to the global object, which is window
in browsers and global
in Node.js.
function foo() {
console.log(this === window);
}
foo(); // prints "true"
But what if the function is part of an object? As it turns out, the this
value is determined by how the function is called, and nothing else. If the function is being directly referenced as a property on an object, then the this
value is that object, otherwise it's global. To put it another way, if you have foo.bar
or foo["bar"]
, then this
is set to foo.
var foo = {
bar: function () {
console.log(this === window);
console.log(this === foo);
}
}
foo.bar(); // prints "false" then "true"
OK, seems simple enough, but what if that function is referenced indirectly? Since the this
value is determined only by how it was called, that means it doesn't actually matter if it's a part of an object or not! If we assign the function to a variable and call it that way, then JavaScript follows the rules for simple functions and sets this
to the global object:
var foo = {
bar: function () {
console.log(this === window);
console.log(this === foo);
}
}
var bar = foo.bar;
bar(); // prints "true" then "false"
foo.bar(); // prints "false" then "true"
That's it. That's how this
works. There really isn't anything else to it, unless you manually change the this value yourself.
Changing the value of "this"
There are three methods that all functions have that allow you to change the this value: call
, apply
, and bind
. Call and apply are more or less the same, they just have a slightly different signature. The first argument to both methods is the value that you want for this, and after that is the arguments to the function.
var x = {};
function foo (y, z) {
console.log(this === x);
console.log(y + z);
}
foo.call(x, 5, 5); // prints "true" then "10"
foo.apply(x, [ 5, 5 ]); // prints "true" then "10"
They each have their uses, so pick whichever one fits your needs best. One cool thing you can do with apply is to forward arguments to another function without needing to know anything about them, which is really handy in prototypal inheritance:
function x() {}
x.prototype.hi = function (a, b) {
console.log(a + b);
}
function y() {}
y.prototype = new x();
y.prototype.hi = function () {
x.prototype.hi.apply(this, arguments)
}
var z = new y();
z.hi(5, 5); // prints "10"
The third function, bind
, works a little differently. When you call bind, it doesn't actually call the function, but instead returns a new function that will have its this
value set to whatever you supplied to bind so that you can call it later:
var x = {};
function foo() {
console.log(this === x);
}
var boundFoo = foo.bind(x);
foo(); // prints "false"
boundFoo(); // prints "true"
Note: bind is in ECMAScript 5 only, so it doesn't work in IE8 and older, but fortunately underscore.js has an implementation that works in older browsers (_.bind
).
Callbacks
The way that this
works becomes really annoying when you're dealing with callbacks because the this
value is set to the global object unless the underlying event firing mechanism changes it for you.
var x = {
foo: function () {
console.log(this === x);
console.log(this === window);
setTimeout(function () {
console.log(this === x);
console.log(this === window);
}, 1000);
}
};
x.foo(); // prints "true" then "false", then about a second later prints "false" then "true"
A lot of people do something like:
var self = this;
and then reference self
in their callbacks. It works, but feels a little hacky. Instead, you can also do:
var x = {
foo: function () {
console.log(this === x);
console.log(this === window);
setTimeout(function () {
console.log(this === x);
console.log(this === window);
}.bind(this), 1000);
}
};
x.foo(); // prints "true" then "false", then about a second later prints "true" then "false"
Now the this
value is what we want it to be for our callback. It's worth point out though that aliasing this
to self
does have one nice advantage: self
can be minified to a single character, whereas this
will always be 4 characters since it's a keyword. If code size is critical, then aliasing this to self almost always results in slightly smaller code size.
One last note about libraries and callbacks: many libraries use apply and call to set the this value to something more useful than the global object, so make sure you read the documentation to make sure that you are getting the this
value you expect. If you want a this
value other than what the library sets, just use .bind
or _.bind
. In the case of nested applys, calls, or binds, the last one wins, so you can always set it to whatever you want.
I hope this helps demystify how this
works in JavaScript!