The this keyword in Javascript has a certain reputation for being awkward. Fortunately, it’s very easy to learn how this works, and then leverage it for your benefit rather than detriment.
Let’s start with some code to play around with:
name = 'Global'; function myClass() { this.name = 'My Class'; this.getMyName = function() { return this.name; }; } function person() { this.name = 'Bob'; } var classInstance = new myClass(); var nameMethod = classInstance.getMyName; var personInstance = new person();
In this example, the function ‘getMyName’ gets the name value from ‘this’. Let’s see what it outputs:
console.log(classInstance.getMyName()); // My Class
So far, so good. But what if we call the method directly, and not via the class?
console.log(nameMethod()); // Global
Uh oh, we’re seeing the global ‘name’ value, not the one for the class we set up. What’s happening? Well, this is actually passed in implicitly as the first argument to the function – you just never see it. By default, this is the target you call the function on. So in reality the function definition and execution actually look like the following:
function(this) { return this.name; }; console.log(classInstance.getMyName(classInstance));
When you call the method directly without the classInstance receiver, the global scope is passed in as this. As a result, we can do some pretty clever things. Remember how we have another class called ‘person’? We can detach the method from myClass and call it using person:
console.log(nameMethod.call(personInstance)); // Bob
Hang on, what’s this ‘call’ method? Call allows you to explicitly set the this value. The first parameter will be mapped to this, while all subsequent parameters will be passed directly to the function. If you don’t specify a parameter, call will implicitly use the global scope:
console.log(classInstance.getMyName.call()); // Global
Pretty cool, huh? This is how jQuery allows you to use this to refer to the element you’re working on with your anonymous functions and the like.
But, what if we want getMyName to always refer to the name within the class? For this we’ll want to ‘capture’ the this variable so that it never changes:
function myClass() { self = this; this.name = 'My Class'; this.getMyName = function() { return self.name; }; }
By copying this into a local variable at instantiation, we can then bring it into the getMyName function via a closure. Now the output is as expected without any this shenanigans:
console.log(classInstance.getMyName()); // My Class console.log(classInstance.getMyName.call()); // My Class console.log(nameMethod()); // My Class console.log(nameMethod.call(personInstance)); // My Class