March 22, 2016

Javascript Methods, Functions, and Constructors

One of the most confusing parts of Javascript (for me at least) is keeping track of what this refers to.

I always end up referring back my copy of Javascript: The Good Parts, which, by the way, I think is a must read for any web developer.

Just recently, I went back and refreshed my memory about this, methods, functions and constructors and I decided to jot down some notes here, hope this is helpful.

Four Ways to invoke Functions

In Javascript, these are the 4 contexts in which functions can be evaluated:

  1. method invocation
  2. function invocation
  3. constructor invocation
  4. apply invocation

Method invocation

When a function is stored as a property of an object, it's referred to as a "method" and this is bound to the object. Here's an example of a method named setName:

var person = {name: "Harry", 
              setName: function(name) { 
                // `this` refers to object
                this.name = name;
              }
             }

And here's how the setName method could be called:

> person.name
"Harry"
> person.setName("Hermione");
> person.name
"Hermione"

Function Invocation

A function is considered a "method" when it's defined as a member of an object. But when a function is defined anywhere outside the context of an object, the this keyword is bound to the global object.

Here's an example of a function:

var myFunction = function() {
  // `this` bound to global object
  this.isnt_very_useful = "hello";
};

Things get really confusing when you define a function inside a method.

// 'setName' is a method because it's tacked onto an object
person.setName = function(name) {

  // 'this' refers to the person object. Here's a trick so
  // that we can reference the thing that 'this' points to
  // here later.
  var that = this;

  // here's a function inside the 'setName' method
  var setDefaultName = function() {
    // `this` refers to global object!
    this.name = "this isn't what we want!";

    // Good thing that we bound the value of 'this' from the 'setName'
    // method to a new variable named 'that' earlier, because now we
    // can use 'that' to access the thing that you probably think that
    // 'this' should actually refer to!
    that.name = "Draco";
  }

  if (this.name == undefined) 
    setDefaultName();
};

Confusing, eh?! Just remember that this inside functions always refers to the global object.

Here's some proof that the that trick worked:

> person.setName();
> person.name
"Draco"

And remember when we assigned 'this' inside the 'setDefaultName' function? Check this out:

> this.name
"this isn't what we want!"
Didn't see that coming, did ya?!

Constructor Invocation

When a function is called with the new prefix, it's referred to as a constructor. Calling functions using new creates a new object. Inside constructors, the this keyword is assigned to the new object.

The confusing part here is that if you accidentally forget to use new, then unexpected things happen. I never use constructor invocation, it's too easy to forget new. But here's an example:

// Here's a constructor
var Person = function (name) {
  this.name = name;
};

// And here's a method on our Person Class
Person.prototype.getName = function() { return this.name };

Here's an example to prove this works the way I think it does inside constructor functions:

> var person = new Person("Dave");
> person.getName();
"Dave"
> var person = Person("Dave");
> person.getName();
Uncaught TypeError: Cannot read property 'getName' of undefined

See the scary error message when I forgot to use new?!

Apply Invocation

Finally, for full control, you can use apply to manually evaluate functions. The apply function takes 2 arguments. The first argument controls what this will be bound to. The second argument is an array of args to pass to the function.

Let's define a cat object:

var cat = {"name": "shubie"};

Now, notice that our cat object doesn't have a getName method defined. But check this out! We can reuse the getName method from the Person object above on the cat like so:

> Person.prototype.getName.apply(cat);
"shubie"

By passing the cat object as the first argument to apply, we made sure that apply treats cat as this when evaluating Person.prototype.getName. Pretty crazy, right?

Summary

Understanding the 4 contexts in which functions can be called in javascript, will help you to understand the this keyword.

Also, like I mentioned, Javascript: The Good Parts, does a great job of explaining all this and more. Mozilla has nice documentation here as well.

Tags: software javascript