The confusing THIS pointer in class functions

Topics: Language Specification
Mar 22, 2013 at 3:14 PM
In order to force a class function THIS pointer to refer to the class instance in typescript we must implement the function body in the constructor as:

example function onTabSelected declared in the class as
    onTabSelected(e: any): void {
        // implemented in constructor for closure purposes
    }

    constructor
    {
        this.onTabSelected = (e: any) => {
           // Body of function here
    }
Needless to say this is quite inelegant, unnatural and complex.

The THIS issue will probably prove to be the single most confusing problem in TypeScript for those who come to it hoping to avoid JavaScript "strangeness".

Perhaps it would be clearer to have a function declaration within a class that explicitly binds THIS to the class instance where the function is declared regardless of where that function is called.

Something like:

Local myFunction(arg1:any): number {}; (Local or Self)

or

Self.myFunction(arg1: any): number {};

When called as a callback, for example with JQuery, the THIS pointer would always be the class instance.
These functions, as an added benefit, would not require the use of THIS when referring to class owned members. The Constructor should be local be default.

An alternative, cleaner solution could be to declare functions that do not want to bind THIS to the class instance with a modifier like using the FUNCTION keyword to indicate respect of the JavaScript binding model:

Function myFunction(arg1:any): number {}; // acts as a JavaScript function
myFunction(arg1:any): number {}; // Binds this the class instance

I believe that learning TypeScript would be a lot easier and the language much cleaner and frankly logical.


Philippe Moransais
Coordinator
Mar 22, 2013 at 4:50 PM
Hi Philippe,

TypeScript actually went through a couple class designs, but ultimately we aligned with ES6, the new version of JavaScript currently being designed. It's easier for JavaScript programmers to just have to learn one style which works in both places. One of the side effects here is that you end up thinking about JavaScript when you write TypeScript because TypeScript is still just JavaScript under the surface. Using 'this' follows the same rules as JavaScript, so if you wanted to capture a particular instance of 'this', you would use a different pattern (like the one you describe).
Mar 22, 2013 at 10:54 PM
Hello people.

If we need JavaScript we will write JavaScript. But we need modern sane elegant strongly typed CLASS based language like ActionScript. If we can't access class context in method it means this not a method — this is just a function with implicit parameter this. Please make something syntax sugar to bind method to it's class.

Thanks.
Mar 25, 2013 at 2:51 PM
Hi Jon,

Dubrovsky tells it quite well! We need a "modern, sane and elegant language" to program HTML/CSS based applications and typescript is only partially delivering on that.
JavaScript is important because we cannot go around it. Since we cannot wait for ever on comities and browsers, the only way to move forward is to consider JavaScript as an intermediary language that we compile to. No sane person programs directly in .net IL. Your beautiful C# and Visual Basic compilers do that.

JavaScript cannot be allowed by default to be the foundation of the future of application development! To me, the only reason to keep Typescript close to JavaScript is to preserve full access to the JavaScript ecosystem.

I think I understand why Microsoft is trying to align with the standard, but I believe that in this case it is a strategic mistake. The application landscape has changed and is incredibly fractured. For the fist time in years developing within the Microsoft ecosystem is just too limiting in terms of reaching all platforms. Even the Windows development story is hopelessly fractured (Classic Desktop, Windows 8 Apps, Windows Phone), and that environment is quickly loosing market shares. Basically, our clients want their apps on IPads, and one way or the other they will get them there.

This new landscape is not favoring Microsoft. Many long time Microsoft developers are reevaluating their technology choices because Microsoft is no longer delivering a development solution that provides apps with sufficient reach.

This is why I think typescript is a critical product and why I fear that you are being too cautious.

This is just my two cents, but imagine an environment centered on a new .net, call it @net, which is based on typescript which provides the best of the .net libraries (rewritten in typescript and including client based Linq and an Entity Framework context with transparent socket optimized server access), and which preserves access to the full JavaScript ecosystem and allows pure JavaScript as well as C# and even VB like syntaxes (you know, just like .net).

Imagine also that typescript could be used to program on the server directly. I mean that we would have an environment with a client and a server project which use the same language on both and leverage all Microsoft technologies (SQL, Azure) in a dramatically simplified and optimized way.

Imagine now that this environment could also offer a framework like Silverlight with best of class client controls that would be able to create applications for the web, Windows 8 and through a phone gap like solution even to apps. By creating a VM, this could also provide 'native' apps on many platforms.

I hope Microsoft is secretly building something like this. The company needs it and we developers need it desperately too.

The key, is to transfer into Typescript most of the progress Microsoft has made in the last 20 years.

Please let that be. Be bold.

Philippe Moransais
Mar 25, 2013 at 3:43 PM
Edited Mar 25, 2013 at 3:44 PM
Philippe

Uhm... There are quite a few solutions that has taken the path of a new language witch treats JS as an intermediate language... I suppose that Coffee script is the more successful one here. Dart never really landed imo... and then there is the forest of C#, Java, ...insert other lang here... to JavaScript compilers...

I think that all those has more than shown that they just don't gain that much traction in the end...
I think the choice MS have made here, making TypeScript so closely aligned to JavaScript is what will give it a chance to succeed where others have failed...

Besides, the new range of markets surely makes things more difficult, but funny enough, Microsoft are now the only one who actually has gone down the road of supporting JavaScript/HTML as a "native" choice for Windows 8/RT Application development... For Android and iOS your still forced down a workaround of creating a Objective-C or Java where you use a WebView or alternatively one of the many cross-compilers... Unfortunetly for us developers, the consumers doesn't really care what pains we have to go through to provide them with their iPhone, iPad, Android etc. apps...

Besides, Dubrovsky's comment does indeed say it well... The thing is just that some of us actually do wan't to write JavaScript... We just happen to really enjoy the optional type layer on top of this... That why I am Happy with TypeScript and would NEVER use Coffee Script, Dart etc...

But Opinions are Opinions...
Mar 25, 2013 at 4:08 PM
Hang on a minute, I'm confused. I thought that in methods defined on a class, this referred to the instance of the object. For example:
class Dog {
    private _isDead = false;
    shoot() {
        this._isDead = true;
    }
    get isDead() {
        return this._isDead;
    }
}

var dog = new Dog();
dog.shoot();
assert.isTrue(dog.isDead);
In what situation would the assert in the code above fail?
Mar 25, 2013 at 4:38 PM
jmelgaard

It seems that in this debate the opinions are forged by background and our various experiences.

I personally get uncomfortable when I need to type === to check for equality. Having to use 'this' to refer to the rest of my class is very awkward and inelegant. My unease rises when I have no clear way to immediately know the context my functions operate in and the only "pattern" available is to declare them in one place and create their body in a constructor. It gets even worse when I realize I can obliterate system variables without warning. Books have been written about this.

I did not adopt Dart or Coffee Script because of fear of ending in a dead end.

My point is that I whish Microsoft would try to give us the best of both worlds like they did in .net (many more worlds there including Cobol).
I am advocating for a typescript that lets us have full JavaScript compatibility if we want or need it, while also allowing a "modern, sane", highly productive and safe C# syntax and compiler assistance. It would be a matter of declaring classes or blocks as JavaScript compliant or typescript (probably more complicated that I can imagine, but the team that gave us Linq can surely do anything).

It is becoming clear that Entreprise applications will have to choose between a closed existence within Windows, a closed existence as Android or IOS apps or an open and ubiquitous life on the Internet.

The millions of Microsoft developers need tools so they can contemplate the later. This is why I think typescript's (or other similar efforts) direction may well be critical.

You observe very justly "Unfortunetly for us developers, the consumers doesn't really care what pains we have to go through to provide them with their iPhone, iPad, Android etc. apps...". I was just adding by two cents in the hope that Microsoft would care about our pain and do something about it.

Philippe
Mar 25, 2013 at 4:48 PM
@markrendle the thing they're after is this:
function foo(bar: () => void){
    bar();
}
var dog = new Dog();
foo(dog.shoot);

assert.isTrue(dog.isDead); // Fails
Furthermore it appears they also don't want to do this:
    shoot() {
        this._isDead = true;
    }
and instead write
    shoot() {
        _isDead = true;
    }
IMO, there are many other interesting problems that the TypeScript team could spend their time solving than either of these (dare I say newbie) issues.
Mar 25, 2013 at 4:59 PM
Markrendle,

The problem in my post arises when you provide a typescript function as a call back that will be triggered by a library. JQuery or wijmo for example will change the context of the 'this' pointer from the class that contains your function to the DOM element that JQuery selected and operated on (at least I think this is what happens).

If you need to refer to a class or module level variable in that function well you can no longer do it unless you reestablish the context of 'this' to the class. This is what I understand but it is quite esoteric to me. Anyway, you need to do some strange stuff like I describe in my original post because the class context is gone.

Interestingly , if you do try to use your variables, the typescript compiler has no way of knowing that you cannot because it does know what 'this' will end up refering to. You will have a run time issue with an undefined variable. That will not crash either. It will just be wrong. It's like doing trapeze without a net: heroic and stupid.

typescript provides the (attr) => {} function declaring syntax to coerce the 'this' pointer. But I could not make it work with wijmo (may be because you have to specify the call back functions in a JavaScript object.

So, in order to be fully JavaScript compliant we have two function declaring syntaxes in type script:
myFunction () {}
myFunction() =>{}
and also the javascript form: Function myFunction() {}

Philippe
Mar 25, 2013 at 5:17 PM
Nabog,

It is deeper than that, even though style and elegance does matter in life and this.myvar is neither stylish nor elegant.

I am a JavaScript newbie (not completely though) but I have 30 years of programming in a number of other languages and technologies.

I stumbled onto the following problem:

Let say that we write a Class with a function onClick that needs to count the clicks and needs to save the increment to a class variable myCounter declared on the class.

The onClick function must be used as a call back from a wijmo widget. It turns out that the context of 'this' will be changed by the library and the beautiful and elegant construct this.mycounter will be undefined (without ever having a compiler or interpreter error thrown.

The only way I found to make this work was to declare the function with an empty body in the class and to assign the body from within the constructor.
I also had to declare a var self = this to capture the class context so that I could do self.mycounter. Jon called that a pattern. To a javaScript newbie this is a cluster...



Contrary to your thinking, I believe that typescript should correct all of these JavaScript flaws. I think it is essential as I have said before.

Please if you know of a better solution to my problem share it with me.

Thanks

Philippe
Mar 25, 2013 at 5:40 PM
Something like this might work;
class Foo {
    public myCounter: number = 0;
    public onClick(){
        this.myCounter++;
    }
} 

class WijmoWidget{
    public bar(baz: () => void){
        baz();
    }
}

var foo = new Foo();
var widget = new WijmoWidget();

widget.bar(foo.onClick.bind(foo));

console.log(foo.myCounter); // 1
The idea is to bind the context to the callback before passing it to the wiget.
Mar 25, 2013 at 6:48 PM
Ah, I see. That's OK, I thought I'd misunderstood something fundamental about TypeScript.

I think nabog's foo.onClick.bind(foo) example is the correct approach. Assigning functions as properties of an object in the object's constructor is going to create potentially hundreds of copies of those functions, so no.

As for all the people wishing that TypeScript were something else, some magical thing that was simultaneously C# and JavaScript and Dart and Smalltalk, well, I'm British and old enough to remember this:

http://www.youtube.com/watch?feature=player_detailpage&v=ERDUbAv8Qz0#t=44s

TypeScript cannot "correct JavaScript's flaws" while maintaining its aim of being a strict superset of JavaScript (and ECMAScript 6), and that aim is surely fundamental to any chance of success TypeScript may have in the world.
Mar 25, 2013 at 8:54 PM
nabob

I did not know about the ability to bind a different context to a function.
It does resolve the issue.

Would it not be preferable for the library to pass its return object explicitly as a parameter of the function, in full view, rather than altering the context of the this keyword? The variable function scope will prevent typescript from doing any type checking on the scope and leave opened the possibility for run time bugs, in part defeating one of typescript purposes.

Anyway thanks for your help. The solution is very clean.

markrendle

Thanks for the you tube laugh. I am not looking for the moon, just for a development platform that makes HTML/CSS/JavaScript development productive, safe and easy. That will not happen with typescript if the language remains a superset of JavaScript. There would be nothing magical in making typescript able to check strict JavaScript in some modules, classes or blocks marked as strict and provide what is the real magic, a language that benefits from 20 some years spent perfecting modern languages from Pascal to Delphi and c# while remaining CLOSE to JavaScript. It could still be used only for type checking and inference and code structuring with modules and classes by making all modules STRICT or users could benefit from a more advanced development experience where they do not need JavaScript compatibility.

It would still produce pure JavaScript.

I'll concede that it may well not make typescript successful with JavaScript programmers, but is that Microsoft's only target?

Philippe
Mar 25, 2013 at 9:34 PM
nabog wrote:
The idea is to bind the context to the callback before passing it to the wiget.
Alternatively you could pass the context to the method and then function.apply() it. I do this quite a lot as it allows me to avoid using bind in a method call, avoids un-necessary anonymous function creation and allows me to swap the context to whatever I need at the time.

Or alternatively you don't use callbacks at all but some other method like events or signals. It would be cleaner if Foo was listening for a custom "count me!" event which Widget dispatched, or listened for a Signal directly from Widget.

It becomes harder when you have to integrate with 3rd party libraries outside of your remit of course, but if you've complete control over the flow then there is no reason why you can't design something more elegant from the get-go.
Mar 25, 2013 at 11:42 PM
I've been wrapping callbacks in lambdas to get around the this-binding issue, e.g.:
class Foo {
    public myCounter: number = 0;
    public onClick(){
        this.myCounter++;
    }
} 

class WijmoWidget{
    public bar(baz: () => void){
        baz();
    }
}

var foo = new Foo();
var widget = new WijmoWidget();

widget.bar(() => {
    foo.onClick();
});

console.log(foo.myCounter); // 1
It works, but is there any reason to prefer the '.bind(foo)' method (other than the marginal gain in brevity)?
Mar 26, 2013 at 11:00 AM
@dbbrown,

Brevity is actually not a factor because the callback wrapper can be abbreviated to
var foo = new Foo();
var widget = new WijmoWidget();

widget.bar(() => foo.onClick());
The callback wrapper is also faster than bind.

However, the callback wrapper creates a closer coupling between the Widget and the client, for example, when the widget is generating data that needs to be passed into the callback, then we must write:
class Foo {
    public myCounter: number = 0;
    public fooVal:string;
    public onClick(one:string, two:string, three:number){
        this.myCounter++;
        // Do something with parameters
    }
} 
class WijmoWidget{
    public bar(baz: (one:string, two:string, three:number) => void){
        baz("one", "two", 3);
    }
}

var foo = new Foo();
var widget = new WijmoWidget();

widget.bar((one:string, two:string, three:number) => foo.onClick(one, two, three));
Whereas with bind one may simply write:
widget.bar(foo.onClick.bind(foo));
and still be able to change both the client and the Widget without breaking the binding.