Skip to main content

JavaScript - The this keyword

"this" is one of the most misunderstood construct in JavaScript.  To understand this first lets go through how to create a construction function in JavaScript.  A constructor function is a function which is used to create instances of objects in JavaScript.

You define a constructor function using the same notation that you use to define a normal JavaScript function.  The convention to follow is to capitalize the first letter of the function name.

This requirement is not enforced by the JavaScript language but it is a generally accepted practice and there are many benefits which we will shortly discuss.

Let's define a constructor function to hold our menu information.

function Menu() {

}

So, in the above snippet you have a constructor function named Menu defined. At present this function doesn't do anything good.

Let's see how to invoke this function

var menu = new Menu();

Let's add some public properties to this function.
function Menu() {
    this.menuName = "File";
}

Let's create an instance and see how we can use this.

var menu = new Menu();
alert(menu.menuName);

If you run the above code, you should get an alert with a value "File".  This is not so interesting.

Let's pass, in the name information to the function.

function Menu (name) {
    this.menuName = name;
}

Now you can call this function as shown below.

var menu = new Menu("file");
alert (menu.menuName);  // You will still get an alert with a value 'file'

Isn't this interesting now.   Now the point to remember is that nothing in JavaScript prevents you from invoking this function directly.

For e.g.

var menu = Menu("file");

What do you think the output of the above will be if you do an
alert(menu.menuName);

You will get an error similar to "Uncaught TypeError: Cannot read property 'menuName' of undefined".

You can see this in action here




Why?  Any idea?  Also, there is a side effect to this.   You accidentally created a global variable "menuName".

You don't believe me.  Try doing an

alert(menuName);   // OR
alert(window.menuName);

Ok. Lets get a bit deeper into how this thing work.  By default a constructor functions always returns an instance of the function object  on which the "new" is called upon.

If you directly invoke a constructor function without creating a new instance, then that function is executed rather than an object being constructed. So, the return value of that function invocation will be undefined as you are not explicitly returning any value from the function.

So, all assignment to "this". goes to the global window object and you accidentally create globals.
Always name your constructor function starting with an uppercase.
The primary reason why this is an issue is because JavaScript don't force you to construct object of constructor function as for JavaScript execution engine there is no difference between the two.

This is where naming convention plays an important role. Name all your constructor function starting with an uppercase letter. This way it will be very easy for you to detect if there is any unintentional misuse of the constructor function. Besides there are certain tools like "jslint" which warns you if you directly invoke a constructor function which has a name starting with uppercase.

Ok, so how to avoid this problem.  What we want is whenever the user uses the Menu function either as direct function call or as a constructor function we need the function to return a menu object to the user.

A simple solution is to return an anonymous object from the constructor function with required properties attached to it.

function Menu(name) {
    return {
        menuName : name
    };
}

We can use this function either by direct call, or through constructor invocation, we will always get the correct object, without creating globals.

Why this works is because constructor function by default returns the instance of the object on which it is invoked.  The other interesting thing is we can return any thing from this constructor function.  We will use this feature to return correct object back to the caller.

var menu = Menu("file");  / called directly
alert(menu.menuName);  // alerts 'file'

var menu1 = new Menu("Help");  // call with new
alert(menu1.menuName); // alerts 'Help'

// alert(menuName);  // See no, globals

this Gets tricky


Take this example below. We have a simple function which returns stock quote. Pretty simple example in this case.

function StockQuote() {
    this.quote= "12,13,343,343";
    this.getQuote= function() {
         //this is expected to be a reference to the current instance
         return this.quote; 
    }
}

Now lets' say we need to get the quote in the next 3 seconds..

var quote = new StockQuote();
// call the getQuote function in 3 seconds.
setTimeout(quote.getQuote, 3000);

Many beginner JS dev may think this may work, but it won't. It'll give an error saying that quote is undefined. This is because there is no binding of functions to instances in JavaScript. Whenever the instance isn't explicitly stated, then "this" becomes windows object (at least in the browser, as js runs in other environment as well). Writing quote.getQuote() indicates that you want "this" to belong to quote, so it will work correctly. However, the setTimeout function only has a reference to that function. When it calls it, after 3 seconds, it is not aware of "quote", so JavaScript sets this to window.

"Fixing" this

There are several ways of forcing this to be what you intend to be and many of them uses some unique features of JavaScript.

The three important ones are
  • apply
  • call
  • bind

apply is a member of every function. It says "invoke this function on this object (change the context of the object).


Before solving the setTimeout problem lets briefly have a discussion on Lexical Scroping in JavaScript.

Lexical scoping allows you to access local variables in a parent function by the child functions. i.e. if a function is defined withing another function, it can access its own local variables as well as those of the function it was defined within.

Now let's solve the setTimeout problem with Lexical scoping.

var myQuote = new Quote();
setTimeout( function() {
    myQuote.getQuote();
}, 3000);

We created a new anonymous function which we are passing to the setTimeout. Our new function can access myQuote, so it just applies it to the getQuote function.

Solving setTimeout problem using "bind".  Here "bind" is more useful than the other two methods.

var myQuote = new Quote();
setTimeout(myQuote.getQuote.bind(myQuote), 3000);

The bind() method returns a new method with the context changed based on the first parameter that is passed.

The difference between call and apply is subtle and only varies in the way parameters are passed. apply requires the second parameter to be array which represents the parameters to the function , and call accepts an argument list.

Let's quickly look at the syntax of both the functions.
fun.call(object, arg1, arg2, ....);

fun.apply(object, [argsArray]);

Hope you have enjoyed this post.

In subsequent post we will look at how to avoid this edge cases by using various well known pattern and practices.

Comments

Vitamin A said…
This writing has inspired me to start focusing on my own blog
Cedrick Debaets said…
Hi, i think that i saw you visited my website thus i came to “return the favor”.I'm attempting to find things to enhance my website!I suppose its ok to use some of your ideas!!
Thank you for each of your hard work on this web page. Kate really likes working on internet research and it's really obvious why. My partner and i notice all about the lively form you convey efficient items via this blog and therefore invigorate participation from some others on that concept while my simple princess is really discovering a lot. Take advantage of the rest of the year. You have been carrying out a fabulous job.
Asa Zelada said…
This is very interesting, You're a very skilled blogger. I have joined your feed and look forward to seeking more of your magnificent post. Also, I have shared your web site in my social networks!
Iris Fansler said…
Hey There. I found your blog using msn. This is a really well written article. I will be sure to bookmark it and come back to read more of your useful information. Thanks for the post. I will definitely return.

Popular posts from this blog

JavaScript Scope

In this blog post we will dig deeper into various aspects of JavaScript scope.  This is a pretty interesting topic  and also a topic which confuses many beginning JavaScript programmers. Understanding JavaScript scope helps you write bug free programs (hmm.. atleast helps your troubleshoot things easily) Scope control the visibility and lifetimes of variables and parameters.  This is important from the perspective of avoiding naming collisions and provides memory management service. Unlike other languages, JavaScript does not have block level scope.  For e.g. take for instance the following piece of c# code. public void Main () { int a = 5; if (true) { int b = 10; } // This will throw compile time error as b is not defined // and not within the scope of function Main(); Console.WriteLine(b); } If you write the same code in JavaScript, then the value of 'b' will be available outside the 'if' block. The reason for this is JavaScript does no

JavaScript Function Spaghetti Code

In this post we will have a look at the spaghetti code created by functions and how to avoid them. First lets quickly go through why this is a cause of concern. Problems with Function Spaghetti Code Variables/ functions are added to the global scope The code is not modular There's potential for duplicate function names Difficult to maintain No namespace sense. Let's take for example the following set of functions and check whats the issue with them. // file1.js function saveState(obj) {     // write code here to saveState of some object     alert('file1 saveState'); } // file2.js (remote team or some third party scripts) function saveState(obj, obj2) {      // further code...     alert('file2 saveState"); } Now the problem here is if your application is using saveState() then the execution of saveState() which one to call is determined by the script loading.  The later script overrides same functions already defined by earlier script. F

JavaScript for Web and Library Developers

This is my first blog post on my home domain and in this series of posts I will explore various facets of JavaScript language that will help us become better programmers.  The idea is tear apart the language and understand how to use the features and build libraries like jquery, knockoutjs, backbonejs. The idea is not to replicate this libraries, but understand the inner workings using better JavaScript coding practices, design principles and thereby make the web a better place to visit.. We will be covering the following topics, though not in order and there may be addition to this list. Foundations Patterns Closure this keyword Hoisting Anonymous function Currying Server side JavaScript Canvas etc. Hope you will enjoy this journey with me!