TypeScript taking care of ‘This’ and ‘That’ of JavaScript

The bane of javascript developer is the powerful keyword, “this”. ‘this’ is a variable that’s set when a function is called. it is a very powerful and flexible feature, but it comes at the cost of always having to know about the context that a function is executing in. This can be extremely confusing when a function is used as a callback.

See the simple TypeScript and JavaScript equivalent code. Look for the problem in below code?

TypeScript

class Greeter {
    element: HTMLElement;

    constructor(element: HTMLElement) {
        this.element = element;
        this.element.innerHTML += "The time is: " + new Date().toUTCString();
    }

    start(){
        setInterval(function ()  {
            this.element.innerHTML = "The time is: " + new Date().toUTCString(), 500
        });
    }
}

window.onload = () => ; {
    var greeter = new Greeter(document.getElementById('content'));
    greeter.start();
};

JavaScript

var Greeter = (function () {
    function Greeter(element) {
        this.element = element;
        this.element.innerHTML += "The time is: " + new Date().toUTCString();
    }
    Greeter.prototype.start = function () {
        setInterval(function () {
            this.element.innerHTML = "The time is: " + new Date().toUTCString(), 500;
        });
    };
    return Greeter;
})();

window.onload = function () {
    var greeter = new Greeter(document.getElementById('content'));
    greeter.start();
};

So do you see the problem in above code?

This simple code will not work. This is because the ‘this’ being used in the function created by ‘greeter.start()’ will be set to ‘window’ instead of our ‘greeter’ object. Here is the error message you will see:

Unhandled exception at line 8, column 13 in http://localhost:51370/app.js

0x800a138f – JavaScript runtime error: Unable to set property ‘innerHTML’ of undefined or null reference

This happens as a result of calling ‘start()’ from window.onload. There is no dynamic binding for ‘this’ other than Window. (note: under strict mode, this will be undefined rather than window).

We can fix this by making sure the function is bound to the correct ‘this’ before we return the function to be used later. This way, regardless of how its later used, it will still be able to see the original ‘greeter’ object.

To fix this, you will often see in JavaScript, people use ‘that’ variable to bind the right ‘this’ to it. (SideNote: I will prefer some other more descriptive name than ‘that’, but there are too many people using this to set it as a convention.)

To fix the above JavaScript, we need to add one line in start() as follows, and then in SetInterval refer to ‘that’, so no matter from where start() method is called it will always refer to the right ‘this’.

 Greeter.prototype.start = function () {
        var that = this;
        setInterval(function () {
            that.element.innerHTML = "The time is: " + new Date().toUTCString(), 500;
        });
    };

Now, all this can turn out to be complicated if you have many functions and functions returning functions returning functions etc. However, if you are using TypeScript, you just change the simple function call with a lambda call and everything will be taken care for you. Notice, i am still using ‘this’, in SetInterval. Isn’t it neat?
TypeScript

start(){
        setInterval(() =>;  {
            this.element.innerHTML = "The time is: " + new Date().toUTCString(), 500
        });
    }

Switching the function expression to use the lambda syntax ( ()=>{} ) rather than the JavaScript function expression will automatically capture the ‘this’ available when the function is created rather than when it is invoked.

Now here is new TypeScript function, notice the only lamda difference

class Greeter {
    element: HTMLElement;

    constructor(element: HTMLElement) {
        this.element = element;
        this.element.innerHTML += "The time is: " + new Date().toUTCString();
    }

    start(){
        setInterval(() =>;  {
            this.element.innerHTML = "The time is: " + new Date().toUTCString(), 500
        });
    }
}

window.onload = () =>; {
    var greeter = new Greeter(document.getElementById('content'));
    greeter.start();
};

and here is equivalent new JavaScript function:

var Greeter = (function () {
    function Greeter(element) {
        this.element = element;
        this.element.innerHTML += "The time is: " + new Date().toUTCString();
    }
    Greeter.prototype.start = function () {
        var _this = this;
        setInterval(function () {
            _this.element.innerHTML = "The time is: " + new Date().toUTCString(), 500;
        });
    };
    return Greeter;
})();

window.onload = function () {
    var greeter = new Greeter(document.getElementById('content'));
    greeter.start();
};

Hopefully with this your love for TypeScript increased little more :)

Advertisements

2 thoughts on “TypeScript taking care of ‘This’ and ‘That’ of JavaScript

  1. Pingback: Knockout click event knocking you out | Tech Crumb - collected along the way ....

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s