Binding all member functions to this?

Topics: General
Oct 2, 2012 at 7:46 PM

I read the language specification but couldn't work it out.  How do you ensure that a member function is always bound to the 'this' instance?  This is something CoffeeScript does by generating the necessary bind calls in the function prototype.

Oct 2, 2012 at 8:19 PM

Whenever you declare a class all of its members will be hung off the prototype so any member function will automatically have a proper 'this' pointer.  What you have to be careful about is the use of closures within your methods. So in the example below this.x is visible from within bar() but this.y isn't visible within the callback just as it wouldn't be visible in javascript:

class Foo {
     public x: string;
     public y: string;

     public bar() {
          this.x = 'good';
          setTimeout(function () {
               this.y = 'bad';
          }, 100);
     }
}

To fix the 'this' pointer you can do two things, 1) you can create an external 'var _this = this;' like you would today or 2) you can use a Lambda to declare your callback:

class Foo {
     public x: string;
     public y: string;

     public bar() {
          this.x = 'good';
          setTimeout(() => {
               this.y = 'bad';
          }, 100);
     }
}


If you use a Lambda (TypeScript only feature) they correct the 'this' pointer for you.  

Oct 2, 2012 at 8:27 PM

I realize the use with lambdas.  My point is that some frameworks allow you to bind member functions directly to events (Knockout.JS for example).  In CoffeeScript you can define member functions using the arrow syntax - this ensures that they are always bound to the class instance.  I don't see support for this in TypeScript.

For example:

class fred
{
	private x = "OK";
	constructor() {
	}
	
	public DoFun(s:string):string   {
		return s + this.x;
	}
}

function dostuff() {
	var freed = new fred();
	alert(freed.DoFun("Is"));
	alert(freed.DoFun.call(Window,"Not "));
}

dostuff()

This wouldn't fail in CoffeeScript because it generates this in the function prototype:

  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

   this.DoFun = __bind(this.DoFun, this);


This overwrites the slot for the function on the object with a function bound to the object.

Oct 2, 2012 at 9:04 PM
Edited Oct 2, 2012 at 9:06 PM

Oh I see... That's interesting.  In our project we have a proxy() function that does something similar:

proxy(fn: (...args:any[]) => any, _this: any): (...args:any[]) => any

This guy returns a function that's a wrapper around a class method with a corrected 'this' pointer which we use in our callbacks a lot.  I hadn't thought about just pre-fixing them on the class instance but here's how I'd probably do it:

class Foo {
     public bar: (x:string) => string;
     private y: string;

     constructor() {
          var _this = this;
          this.bar = function (x) {
               return x + _this.y;
          }
     }
}

Obviously not as clean as what yo're looking for but not sure how else you'd do it in TypeScript.

Oct 4, 2012 at 12:32 AM

Just as an addendum and back to the original question: "How do you ensure that a member function is always bound to the 'this' instance?"

We don't.  You can still take a member reference and call it as a function, and 'this' will not be the object instance.

With the arrow syntax "() => {}" we lexically bind this.  This means of course, if your outer this was not the instance (e.g. a method called as a function), then your inner this is also not what you expect.

As outlined above, the only place you can guarantee that 'this' is your instance is within the constructor (if called with new).  Thus using the arrow syntax here also ensures you are binding correctly (as does the proxy and bind methods above).  e.g. to modify the Greeter sample to ensure greet is always called with the correct this:

class Greeter {
 greeting: string;
 greet: Function;
 constructor (message: string) {
  this.greeting = message;
  this.greet = (x: string) => {console.log(this.greeting + x);};
 }
}  
This will automatically capture this in a closure and use it within the function.
var Greeter = (function () {
    function Greeter(message) {
        var _this = this;
        this.greeting = message;
        this.greet = function (x) {
            console.log(_this.greeting + x);
        };
    }
    return Greeter;
})();
Oct 4, 2012 at 1:21 AM

Any chance you could change the syntax a little to add member functions bound to the right type?  Doesn't impact verification if you can't assume the type of 'this' in a member function?

maybe something like:

public DoStuff( x: string ) : number => {
    this.myOtherMEmber();
}

Oct 4, 2012 at 3:50 AM

You got me thinking about this and while TypeScript doesn't have any built in support for this I was able to cook up a little base class that will do what you want.  If you derive a class from the HasCallbacks class below, any method (private or public) pre-fixed with "cb_" will be perminantly bound to it's "this" pointer.  Try running the code below in the playground and you'll see that it works.  It's also effecient in that it only reflects over a class once on first creation and then for each additional "new" you only pay the overhead of binding the "cb_" methods.  Hope that helps... We're planning to switch to this approach in our project as well :) 

class HasCallbacks {
	constructor() {
		var _this = this, _constructor = (<any>this).constructor;
		if (!_constructor.__cb__) {
			_constructor.__cb__ = {};
			for (var m in this) {
				var fn = this[m];
				if (typeof fn === 'function' && m.indexOf('cb_') == 0) {
					_constructor.__cb__[m] = fn;					
				}
			}
		}
		for (var m in _constructor.__cb__) {
			(function (m, fn) {
				_this[m] = function () {
					return fn.apply(_this, Array.prototype.slice.call(arguments));						
				};
			})(m, _constructor.__cb__[m]);
		}
	}
}

class Foo extends HasCallbacks  {
	private label = 'test';
	
	constructor() {
		super();
		
	}
	
	public cb_Bar() {
		alert(this.label);
	}
}

var x = new Foo();
x.cb_Bar.call({});
Oct 28, 2012 at 2:34 PM

Search Internet for "jQuery proxy"

Mar 12, 2014 at 11:30 PM
Old discussion, but for the benefit of people continuing to arrive here via G ... er ... Bing, fat arrow methods can be created outside the constructor as of TypeScript 0.9.1 and will capture this as desired.