johnkors

John Korsnes

JavaScript and the 'This' Keyword

| Comments

I just started reading Christian Johansen’s book ‘Test Driven JavaScript Development’, and read a little snippet I found rather interesting about the ‘this’ keyword and it’s behaviour depending on execution context. Let’s see a small example :

Circle.js
1
2
3
4
5
6
var circle = {
  radius: 6,
  diameter : function(){
    return this.radius * 2;
  }
};

Now, calling the diameter function on the circle behaves as expected (coming from a OO language as C#/Java).

Mocha test #1
1
2
3
4
5
describe('diameter', function(){
  it('treats circle as this', function(){
    circle.diameter().should.equal(12);
  });
});

Fine and dandy. But when we call the diameter function in a different context with out a radius, this will not work:

Mocha test #2
1
2
3
4
5
6
7
describe('diameter', function(){
  it('no longer treats circle as this', function(){
    var copycat = circle.diameter;
    var result = copycat();
    isNaN(result); // no radius on ´this´
  });
});

The this value is actually decided when entering the execution context. In test #2, the valuation of this does not have a radius and returns NaN. On the other hand, any other object with a radius defined may copy the function and make use of it:

Mocha test #3
1
2
3
4
5
6
7
8
9
describe('diameter', function(){
  it('works when assigned to other objects with radius defined', function(){
    var otherObject = {
      radius : 4,
      copycat : circle.diameter
    }; // radius is defined, so ´this´ has a radius defined    
    otherObject.copycat().should.equal(8);
  });
});

We now see that this is referring to otherObject, which obviously has a radius, meaning the function to calculate a diameter named copycat will work as expected.

A way of avoiding duplicating the diameter function, is to use the Function.Prototype.call method. One of many traits of using call is that the other object no longer needs a definition how to calculate a diameter at all. It only needs to have a radius.

The call function forces us to be explicit about what this means in the context of calling the function, expecting it as parameter:

Mocha test #4
1
2
3
4
5
6
7
8
describe('diameter', function(){
  it('can be used on other objects using call', function(){
    var otherObject = {
      radius : 9
    };
    circle.diameter.call(otherObject).should.equal(18);
  });
});

Comments