THIS AGAIN

Another issue I bumped into was converting closures to TypeScript classes, ‘this’ again popped up as part of a larger issue.  For example, starting with the following Javascript code

var MyNamespace = MyNamespace || {};

MyNamespace.Widget1 = function(inp,a,b)
{
	var _a = a;
	var _b = b;
	var _inp = inp;

	this.create = function() 
	{
		inp.addEventListener("keydown",this.click);

	}

	this.click = function(e)
	{
		alert("clicked " + _a);
	}

}

I converted it to the following TypeScript.

module MyNamespace
{

	export class Widget1
	{
		private inp : HTMLElement;
		private _a : number;
		private _b : number;
		
		constructor(inp:HTMLElement,a:number,b:number)
		{
			this._a = a;
			this._b = b;
			this.inp = inp;
		}

		public create() 
		{
			this.inp.addEventListener("keydown",this.click);

		}

		private click(e)
		{
			alert("clicked " + this._a);
		}

	}


}

But this has a problem,  I’ve converted a closure style object to a prototype style (what a TypeScript class is converted to) as can be seen here in the generated Javascript:

var MyNamespace;
(function (MyNamespace) {
    var Widget1 = (function () {
        function Widget1(inp, a, b) {
            this._a = a;
            this._b = b;
            this.inp = inp;
        }
        Widget1.prototype.create = function () {
            this.inp.addEventListener("keydown", this.click);
        };

        Widget1.prototype.click = function (e) {
            alert("clicked " + this._a);
        };
        return Widget1;
    })();
    MyNamespace.Widget1 = Widget1;
})(MyNamespace || (MyNamespace = {}));

Using prototype in of itself is of course not a problem. However, since I changed my variables to be properties of ‘this’, a bug is born: ‘this’ in an event handler refers to the object that fired the event (in most browsers), not my object. Therefore the reference to ‘this._a’ refers to a most likely nonexistent property ‘_a’ on the DOM object that fired the event. We need to fix this by calling bind on the event handler function and getting a reference to a function that is bound to the correct ‘this’, like so

module MyNamespace
{
	export class Widget1
	{
		private inp : HTMLElement;
		private _a : number;
		private _b : number;
		
		constructor(inp:HTMLElement,a:number,b:number)
		{
			this._a = a;
			this._b = b;
			this.inp = inp;
		}

		public create() 
		{
                        // use bind to set this to the instance of Widget1 object not the object firing the event
			var click = this.click.bind(this);
			this.inp.addEventListener("keydown",click);

		}

		private click(e)
		{
			alert("clicked " + this._a);
		}

	}


}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

OR

LOGIN OR REGISTER

Registered users with one approved comment can comment without moderation