Thursday, October 13, 2011

Using the JavaScript "this" Keyword In 3 Common Patterns

I'm not breaking any ground here, everything in this post has been discussed before. However, I like to write about things I want to understand better hence the main reason for this post. In this post I'll take a deeper look at using the "this" keyword in three common patterns: Object Literals, Modules aka IIFE's (sometimes called self executing anonymous functions), and Constructor functions. This acts a little differently in all three of these scenarios and I've spent some time on jsfiddle playing with each.

Object Literals

First lets take a look the most straight forward pattern (I know it's not really a pattern but its still a way some developers use to organize their code): the object literal.  In the object literal "this" points to the object's properties, in fact, you have to use "this" to refer to any of the objects.  One important thing to note here is that nested functions DO NOT have access to the objects "this" scope.  Look at the sayHello function in the code below, notice that there is an inner "helper" function.  Trying to access "this" within it would refer to the window object and not the person object.  In order to provide access to "this" a common workaround is to create another variable that points to "this".  Here I'll call it "self".

//this and objects
var person = {
    firstName: "Ryan",

    lastName: "Anklam",

    getFullName: function() {
        //inside an object we need to point to this to access its properties
        var firstName = "Yoko"; //this var is local to the function so, but 'this' still outputs Ryan
        return this.firstName + ' ' + this.lastName;
    },
    
    sayHello : function(){
        var self= this;  
        var inner = function(){
            //this inner function doesn't have privlaged access to the object's vars.  "this" in here actually refers to the "window" object
            return self.getFullName() + " says hello";
        };
       
       return inner(); 
    }
};

http://jsfiddle.net/bittersweetryan/wewY4/

IIFE's / Modules
In the module pattern things get interesting since the context of "this" is really the "window" context,  however, one of the common ways to provide public members to an IIFE is to return an object with public functions.  As we discussed before there is a "this" scope within the object literal. Since everything is nicely encapsulated in the IIFE the object can directly access it's parent function's variables.

var person = (function(){
    var firstName = "Gregg";
    var lastName = "Jennings";
    
    return{
        getFirstName : function(){
            return firstName + ' ' + lastName;
        },
        
        sayHello : function(){
            return this.getFirstName() + " says hello";
        }
    };
})();

One interesting twist that I first saw on Ben Nadal's blog post "Decoding Morse Code With JavaScript" was to use the call() method when invoking the IIFE and passing in a new object for the context of this. What I really like about this approach is that we don't have to return a new object literal, we can just return "this" to provide public access to methods that are assigned to the "this" scope.

var person = (function(){
    //these are private still
    var firstName = "Aaron";
    var lastName = "Rodgers";
    
    //this will be made public by returning this (see the end of the person function to see where this is coming from
    this.getFirstName = function(){
            return firstName + ' ' + lastName;
    };
        
    this.sayHello = function(){
            return this.getFirstName() + " says hello";
    };
              
    return this;
}).call({});  //sweet! we can set this in an IIFE by passing in a blank object literal using the call method

http://jsfiddle.net/bittersweetryan/qzkzy/

Constructor Functions

When dealing with Constructor functions you should be very careful. The "this" context will ONLY exist after a new instance of the constructor is created using the new keyword. Once the new keyword is used to create a new object only functions that are assigned to the "this" context will be made public. In the example below the sayHello function will only be available after the new keyword is used to create a new instance of the function.

//this and a prototype
function Person(){
    var firstName = "Chales";
    var lastName = "Woodson";
    
    //private within Person object
    function getFullName(){
        return firstName + ' ' + lastName;       
    };
    
    //public function that returns the firstName and lastName
    this.sayHello = function(){
        return getFullName() + " says hi";
    }
};

//this will throw an error since it tries to access a property in the this scope which isn't defined without using new
//console.log(Person.sayHello());

var newPerson = new Person();
console.log(newPerson.sayHello());

http://jsfiddle.net/bittersweetryan/45ceQ/
Fork me on GitHub