JavaScript: this
JavaScript is a language steeped in juxtaposition. It was
made to “look like Java,” yet is dynamic and classless. From this origin, we get
the
new operator
and the
this keyword. You are probably used to this referring to
the current instance of a class, so what could it mean in a
language without classes?
In JavaScript, this refers to the object off of
which a function is referenced when it is invoked (unless it
is invoked via
call
or
apply). What this means is that this is not bound
to your function, and can change depending on how your
function is invoked. It also means that
this changes when declaring a function inside
another function (i.e. each function has its own
this), such as when writing a callback. Let's
see some of this
in action:
var obj = {
count: 0,
increment: function () {
this.count += 1;
},
logAfterTimeout = function () {
setTimeout(function () {
console.log(this.count);
}, 1);
}
};
obj.increment();
console.log(obj.count); // 1
var increment = obj.increment;
window.count = 'global count value: ';
increment();
console.log(obj.count); // 1
console.log(window.count); // global count value: 1
var newObj = {count:50};
increment.call(newObj);
console.log(newObj.count); // 51
obj.logAfterTimeout();// global count value: 1
obj.logAfterTimeout = function () {
var proxiedFunction = $.proxy(function () {
console.log(this.count);
}, this);
setTimeout(proxiedFunction, 1);
};
obj.logAfterTimeout(); // 1
obj.logAfterTimeout = function () {
var that = this;
setTimeout(function () {
console.log(that.count);
}, 1);
};
obj.logAfterTimeout(); // 1
The last couple of examples here demonstrate some methods
for making sure you get the values you expect. The first
time logAfterTimeout is redefined, we use
jQuery.proxy
to create a new function which has its
this permanently set to the passed in value (in
this case, the current this). The second time
logAfterTimeout is redefined, we save the value
of this in a variable (named
that in this case, also often named
self) and use the new variable in place of
this.
Now, all of this is to clarify what’s going on when you use
this. However, it’s pretty easy to avoid using
this altogether in your code (especially in the
way I’ve demonstrated above). Instead of using
this.count all over the place, it would have
been much easier if I’d made count a variable
instead of a property, and then I wouldn’t have to use
this to refer to it.
var obj = (function () {
var count = 0;
return {
increment: function () {
count += 1;
},
logAfterTimeout = function () {
setTimeout(function () {
console.log(count);
}, 1);
},
getCount: function () { return count; }
};
}());
If you’re writing your code in this way, the main place
you’ll run into issues with this is when
handling DOM events (where this is the element
on which the event occurred). In that case, just be careful
when using a callback within that event handler, that you’re
not expecting this to still refer to the
element (and use proxy or
that/self if you need to refer to
it).
Finally, as demonstrated in the example, you can use
call or apply on a function to set
its this value. This isn’t often needed, but
you may also want to know that you can use
apply to pass in an array of arguments to a
function (e.g.
console.log.apply(console, [1, 2, 3, 4])).