Monday, October 10, 2011

Are you confused by "this" Javascript?

George Bush found many things confusing!
The keyword "this" is important in javascript but it is also confusing.  The reason is because it's not always easy to ascertain what it actually means. It has different values depending on where it is in a piece of code and more importantly what context the code is invoked in.

In 'Javascript: The Good Parts', Javascript Guru Doug Crockford describes four different invocation patterns, the "this" parameter is initialised differently in all four patterns: the method invocation pattern, the function invocation patter, the constructor invocation pattern and the apply invocation pattern

The Method Invocation Pattern
When a function is property of an object in javascript it is a method. When the method is invoked, the 'this' refers to that object.  For example, in the code below the property getName is an anonymous function.  If the anonymous function wishes to refer to the name variable it uses the 'this' notation.
var myObject = {
    name: "Staveley",
    getName: function () {
        return this.name;
    }
};

console.log(myObject.getName());// Staveley.

It is worth stressing the importance of 'this' in the example above.  If it is not used, the name will be the global one.
name="GlobalName";
var myObject = {
    name: "Alex",
    getName: function () {
        return name; // refers to name in global namespace.
    }
};
console.log(myObject.getName());// Outputs GlobalName not Alex.

The Function Invocation Pattern
When a function is defined not as a method but just as a function (i.e. there is no property defining it in an object),  the keyword 'this' refers to the the global object.
var getNameFunction = function() {
    name: "Alex";
    console.log(">>getNameFunction(),name=" + this.name);  
};
// Invoking getNameFuction() outputs: undefined.  
// Because, this is equal to the global object 
// which has no name property.
getNameFunction(); 

When another object has a property which calls the getNameFunction, the 'this' inside the
getNameFunction refers to that other object.
var obama = {
    name: "obama"
};

obama.getName = getNameFunction;
obama.getName();  // outputs obama!

But, an inner function does not share the method's access to the object as its 'this' is bound to the wrong value.
obama.getFullName = function() {
    var outputName = function() {
        console.log("barack " + this.name);
    };
    outputName();
} 
// Invoking getFullName() outputs barack undefined. Why? 
// Because the innerfunction's 'this' refers to the global variable. 
// The global variable has no name property.
obama.getFullName(); 

The solution is to assign another variable to the value this. By convention this variable takes the name 'that'.
obama.getFullName = function () {
    var that = this;
    var outputName = function() {
        console.log("barack " + that.name);
    };
    outputName();
};
obama.getFullName(); // outputs barak obama

Inner functions confused Obama!
The Constructor Invocation Pattern
Objects can be easily defined using object literal syntax. However, sometimes when many objects of the same type need to be defined there is a need for consistency. There is also a need to avoid repetition. Two good reasons to use constructor functions! 
var Car = function(colour, reg){
    this.colour= colour;  // this refers to the object being created.
    this.reg = reg;
};

var myRedCar = new Car("Red", "00D901");
var myGreenCar = new Car("Green", "00D902");
var myBlueCar = new Car("Blue", "00D903");  

// Now test those assignments worked. 
console.log(myRedCar.colour + ", " + myRedCar.reg);   // outputs Red, 00D901
console.log(myGreenCar.colour + ", " + myGreenCar.reg); // outputs Green, 00D902
console.log(myBlueCar.colour + ", " + myBlueCar.reg);  // outputs Blue, 00D903

Now suppose we want to add a method common to all 'Car' objects. We do:
Car.prototype.getColour = function() {
    console.log(">> getColour(), colour = " + this.colour);
};

In this case, the 'this' refers to the object created with the new prefix
myRedCar.getColour();  // outputs Red
myGreenCar.getColour(); // outputs Green
myBlueCar.getColour();  // outputs Blue

The Apply Invocation Pattern
When Apply (or Call) is used we are allowed to choose the value of 'this'.  To do this,
we simply specify it as an argument.
myRedCar.getColour.apply(myGreenCar);  // outputs green
myGreenCar.getColour.apply(myRedCar); // outputs red

References:
1. Brilliant tutorial on Javascript objects: http://helephant.com/2008/08/17/how-javascript-objects-work/
2. Douglas Crockford: http://javascript.crockford.com/javascript.html


No comments:

Post a Comment