Here’s how I explain this to people:
There’s nothing innately magical, scary, or confusing about this — it’s a feature, after all. I’m going to attempt to describe it as succinctly as possible, so the information in this post will be rather dense. Take your time reading it.
A brief prologue:
A lot of the misunderstanding arises when developers (like me) who are familiar with Object Oriented programming try to understand Javascript in an Object Oriented fashion. Javascript uses objects, but is not Object Oriented.
So, if you have preconceived notions of this, now’s the time to let them go. Take a deep breathe, clear your mind, and let’s get to the bottom of this.
Part One
Whenever a function is called, a variable called this is instantiated, and its value is set to some object. this is just a variable. It follows the same rules of scoping, you can compare it, you can copy it — it’s a bona fide variable.
It’s incredibly useful to imagine this line of code at the top of each function:
var this = <an object as defined by part two>;
this is only special for three reasons:
this = …
//ReferenceError: invalid assignment left-hand side
though you can modify its properties however you want:
this.foo = …
//works fine — this is an object, after all.
Now, let’s find out which object this gets assigned to.
Part Two
By default, this refers to the window object.
There are only two ways that this will refer to any other object besides window:
1) If you call a function as a property of an object, then this refers to that object.
2) Calling the “apply” or “call” method of a function – the object is passed as a parameter.
Code speaks for itself:
1 2 3 4 5 | // Here's a function. /// var log_this = function(one, two){ console.log(this, one, two); } ///////////////////////// |
We’re creating a variable called “log_this” which is a function object.
1 2 3 4 5 6 | log_this("foo", "bar"); // By default, 'this' refers to window. // Will log: window, "foo", "bar" setTimeout(log_this, 1000); // Again, by default, 'this' refers to window // After 1 second, will log: window, undefined, undefined |
Window is “this” by default. We were actually calling: window.log_this();
1 2 3 4 5 6 | var obj = {}; obj.fn = log_this; obj.fn("foo", "bar"); // Calling log_this as property of 'obj' object. // Therefore, this will be obj, and obj will be logged. // Will log: obj, "foo", "bar" |
This is Case 1: We’re calling the function as a property of an object.
1 2 3 4 5 6 7 8 9 | var MyObj = function(){} MyObj.prototype = {fn: log_this}; // Define a function and its prototype. var o = new MyObj(); // Create an object from the MyObj constructor. MyObj.fn("foo", "bar"); // Calling a function as property of 'o' object // Although it's via the prototype, this will still be o // Will log: o, "foo", "bar" |
Just another way to do the same thing.
1 2 3 4 5 6 7 8 9 | var obj = {}; log_this.apply(obj, ["foo", "bar"]); // Applying obj to log_this function // Therefore, this will be obj, and obj will be logged. // Will log: obj, "foo", "bar" log_this.call(obj, "foo", "bar"); // 'call' is exactly like 'apply' // instead of an array, it uses extra arguments // Will log: obj, "foo", "bar" |
The magic of “apply” and “call” let us set what this refers to.
1 2 3 4 | // You can think of apply/call as doing this: obj.temp = log_this; obj.temp("foo", "bar"); delete obj.temp; |
This is basically what “apply” and “call” do.
Conclusion
That’s all there is to it. I don’t expect you to grok this right away — take time to let it sink in and reread it a few times.
If you still don’t understand this, then you might consider brushing up on how objects, functions, and variable scoping works in Javascript. These are all simple topics with simple rules, but with big implications. That’s how Javascript works.
If you have any questions, feel free to leave them in the comments.