28

Closed

Bind "this" keyword in method to current class instance with a keyword

description

I propose to make "bind" keyword for class methods. This way you can bind "this" keyword to current class instance.


class Foo {
public bind someMethod(): void {
    // Here .this is always current Foo instance
}
}


compiles to


var Foo = (function () {
function Foo() {
    this.someMethod = this.someMethod.bind(this);
}
Foo.prototype.someMethod = function () {
};
return Foo;
})();
Closed Jul 28 at 11:18 PM by jonturner
As part of our move to GitHub, we're closing our CodePlex suggestions and asking that people move them to the GitHub issue tracker for further discussion. Some feature requests may already be active on GitHub, so please make sure to look for an existing issue before filing a new one.

You can find our GitHub issue tracker here:
https://github.com/microsoft/typeScript/issues

comments

joewood72 wrote Nov 20, 2012 at 6:21 PM

Wouldn't it be more correct to bind all member functions by default? I think it makes more sense to specify the type of 'this' when it is not going to be the class that the function is defined in. So a syntax more like C#'s 'extension methods' make more sense:

class Foo {
public static someUnboundMethod( this : OtherClass ): void {
    // this would be of type OtherClass and invalid to be called from 'this.' context
}
}

rwaldron wrote Nov 21, 2012 at 6:09 PM

This completely breaks JavaScript own vs inherited property semantics:

var Foo = (function () {
function Foo() {
    this.someMethod = this.someMethod.bind(this);
}
Foo.prototype.someMethod = function () {
};
return Foo;
})();

apisarev wrote Jan 7, 2013 at 2:45 PM

I'm using this approach only for event handlers. This is very handy to bind handler to current instance, if you later want to unbind it.
Using lambda expression you can't unbind handler easily, you need to add a reference for created function.

Here are example. I have a class with mouse click handler and I want unbind it during first call.
I found 3 way to make it:
  1. Add reference to lambda so you can remove event listener later:
class Foo {
private lambdaFunc: EventListener;

constructor () {
    this.lambdaFunc= (e) => { this.clickHandler();};
    document.addEventListener('click', this.lambdaFunc);
}

private clickHandler() {
    document.removeEventListener('click', this.lambdaFunc);
    alert('Hi');
}
}
new Foo();
  1. Add reference to binded method:
class Foo {
private bindClickHandler: EventListener;

constructor () {
    this.bindClickHandler = this.clickHandler.bind(this);
    document.addEventListener('click', this.bindClickHandler);
}

private clickHandler() {
    document.removeEventListener('click', this.bindClickHandler);
    alert('Hi');
}
}
new Foo();
  1. Bind method in constructor:
class Foo {
constructor () {
    this.clickHandler = this.clickHandler.bind(this);
    document.addEventListener('click', this.clickHandler);
}

private clickHandler() {
    document.removeEventListener('click', this.clickHandler);
    alert('Hi');
}
}
new Foo();



I like the last one. But you can do it even better: make bind keyword so you can use it like this

constructor () {
document.addEventListener('click', this.clickHandler);
}

private bind clickHandler() {
document.removeEventListener('click', this.clickHandler);
alert('Hi');
}

pk11 wrote Feb 16, 2013 at 5:11 PM

I think @joewood72 has a point. I would assume that especially in a class this always refers to the right context. So I would make bind-the-current-context the default behavior as well.

pk11 wrote Feb 16, 2013 at 5:13 PM

I think @joewood72 has a point. I would assume, especially in a class, that this always refers to the right context. So I would make bind-the-current-context the default behavior for instance methods as well.

LukeH wrote Feb 26, 2013 at 10:36 PM

I understand the desire to see TypeScript offer further help here. However, we consider it very important to maintain JavaScript runtime semantics and to allow interoperation with existing JavaScript coding/usage patterns. ES6 classes will not do any bind-on-extract, and so TypeScript cannot do this implicitly either.

We may be able to add warnings/errors on attempts to extract a method off a class instance. This would at least bring attention to cases where this bug might occur.

I'm also supportive of the "bind operator" proposal from Dave Herman, though I think it could be modified to offer even simpler syntax for bind that fits the most common use case of method extraction:

http://wiki.ecmascript.org/doku.php?id=strawman:bind_operator

I expect we will look at both the warning and the bind operator as options for TypeScript.

yaakovd wrote Apr 16, 2013 at 10:53 PM

we consider it very important to maintain JavaScript runtime semantics and to allow interoperation with existing JavaScript coding/usage patterns
If so, then why force adding the "this" keyword to every class-member usage, as opposed to JS automatic inference?

We have the following situation:
  1. "this" doesn't add any useful information in determining the execution context; The meaning of "this" can be changed arbitrarily by an external caller.
  2. "this" in syntactically enforced as opposed to JS.
Either make "this" useful, or don't require its inclusion. Currently we have verbose code which does nothing useful.

yaakovd wrote Apr 16, 2013 at 10:58 PM

I'd also like to add a 3rd point to my comment above:
  1. We don't have the self/_this workaround in TS, which could have mitigated point 1.
    It's a really weak point for the language. It should be addresses by offering a real solution.
You shouldn't be afraid to diverge from ES where it's truly problematic. Otherwise you limit the potential impact of TS.

v3nom wrote Apr 22, 2013 at 8:09 PM

I would be happy with Class.this, which would give a closure to class instance as programmer sees it. Just like self pattern. Binding should be left to developer as there already are constructs to handle this in JS.

LukeH wrote Apr 23, 2013 at 5:20 PM

@yaakovd - Not sure I understand the comment about requiring 'this' keyword on every class-member usage. JavaScript has the same behavior - both in ES5 and in ES6 classes. Also not sure I follow the point about not have a self/_this workaround - you can do that if you want in TS as well, or use arrow functions.

I think perhaps you are comparing TS classes to JS code written using the "closure" class style used in some JS code. That style has some benefits, including binding all members by default, but also generally has worse performance and interoperability with other code. Both in ES6 and in TypeScript, we have adopted the more commonly used "prototypal" class pattern as the "class" syntax. This does not itself solve the member binding problem, leaving it the same as it is in existing prototype based JS code.

LukeH wrote Apr 23, 2013 at 5:21 PM

@v3nom - Could you give an example of what the 'Class.this' approach you describe would look like?

v3nom wrote May 4, 2013 at 6:45 PM

The point is that some libraries change 'this' to some specific object and developers do not want to rebind the event handler, they just want to have alternative way of reaching initial this.

Self in JS:
function A(){
  var self = {};
  self.n = 'n';
  self.someBtn_Click = function(){
    // self.n will always be correct
    // 'this' depends on library used to attach the handler
    console.log(self.n);
  };
  return self;
}
or
function A(){
  var self = this;
  this.n = 'n';
  this.someBtn_Click = function(){
    // self.n will always be correct
    // 'this' depends on library used to attach the handler
    console.log(self.n);
  };
}
Possible way of using self in TS:
class A{
  n = 'n';
  someBtn_Click(){
     // A.this.n will always be correct
    // 'this' depends on library used to attach the handler
    console.log(A.this.n);
  }
}
I am not sure what would be the most idiomatic output for this in JS.

robianmcd wrote Jun 1, 2013 at 7:32 PM

I agree with joewood72

LarsCorneliussen wrote Jul 18, 2013 at 8:17 PM

The Class.this looks great.

Having to move all function bodies to the constructor can't be the solution.

georgiosd wrote Aug 1, 2013 at 4:10 PM

Making "self" a keyword that points to the bound "this" sounds the simplest to me

georgiosd wrote Aug 2, 2013 at 5:49 PM

How is this now resolved??

LarsCorneliussen wrote Aug 4, 2013 at 7:51 AM

???

georgiosd wrote Aug 4, 2013 at 9:00 AM

I received an update that it was marked as Resolved!

LarsCorneliussen wrote Aug 4, 2013 at 3:34 PM

sorry @georgiosd; the ??? was my comment to the same - not to your question.

So, @microsoft: how is this resolved? putting all function bodies in the constructor seems quite un-elegant to me.

MartinSGill wrote Aug 7, 2013 at 11:12 PM

How has this been resolved? Is someone just going around randomly closing issues?

I think it might be as a result of this discussion: https://typescript.codeplex.com/discussions/447071

Marking an issue as "Resovled" without any comment on how it was resolved is rather unprofessional, in my opinion, especially considering how many different issues/discussion threads there have been about how to handle "this" in a class context and for event handlers.

Can we please have some information on how this issue was resolved?

LarsCorneliussen wrote Aug 8, 2013 at 7:06 AM

Two days ago, 0.9.1 was released.
It introduces a new syntax, where you can assign lambda functions as class member and have access to 'this'.

georgiosd wrote Aug 8, 2013 at 7:26 AM

Lars, correct me if I'm wrong but this still depends on the methods to be initialized in the ctor rather than in the body of the class?

For those who missed it, go here: http://blogs.msdn.com/b/typescript/archive/2013/08/06/announcing-0-9-1.aspx

georgiosd wrote Aug 8, 2013 at 7:29 AM

Or rather, I don't understand how the new release solves this issue, based on the example given in the post...

LarsCorneliussen wrote Aug 8, 2013 at 7:38 AM

For me it looks like you can define the body, not just the type, OUTSIDE of the constructor.
class Adder {
    constructor(public x: number, public y: number) { }
                                                    ^^^^ constructor ends here
                                                    
    addMembers = (evt: MouseEvent) => console.log(this.x + this.y);
                                                  ^^^^     ^^^^
}
I tried a simpler sample online - seems to work.
class Adder {
    constructor() { }
    addMembers = (evt: MouseEvent) => console.log(this);
}
becomes.
var Adder = (function () {
    function Adder() {
        var _this = this;
        this.addMembers = function (evt) {
            return console.log(_this);
        };
    }
    return Adder;
})();
I think this is a good solution.
There is no way you could scope a prototype-based function with a custom "this"...

georgiosd wrote Aug 8, 2013 at 7:47 AM

Hm, it still requires assignment notation versus the "normal" declaration notation. It's definitely a step in the right direction. Consider:
class Foo {
    constructor() {
        
    }
    
    bar(): void {
        console.log(this);
    }
    
    barAssigned = (): void => {
        console.log(this);
    }
}
Compiles to:
var Foo = (function () {
    function Foo() {
        var _this = this;
        this.barAssigned = function () {
            console.log(_this);
        };
    }
    Foo.prototype.bar = function () {
        console.log(this);
    };
    return Foo;
})();

LarsCorneliussen wrote Aug 8, 2013 at 8:03 AM

Well, ms chose prototypal methods for the "normal declaration" - which is a good choice. But there is no way you can get hold of the real this in a prototype method. The only place you can be sure is the constructor - and there you have to use assignment.

This is then about JS, not TS. Or am I getting it wrong?

What could your example compile to else?

georgiosd wrote Aug 8, 2013 at 10:17 AM

I was hoping for equivalent output for both methods. But perhaps that would break JS semantics or something.

jonturner wrote Aug 8, 2013 at 5:21 PM

A couple factors here. There are a couple ways to get the "right" this (binding or using the constructor). What we settled on was more of the simplest set of changes to accommodate a way of capturing this, by loosening the restrictions around where it can be used.

It doesn't have special syntax, but this is intentional. We'd like to stay as close as possible to the ES6 standard, and I'll expect we'll continue to align as it solidifies, and one thing we're hesitant to do is to come up with even more syntax, like the original "public bind someMethod()". We saw this as the easiest and shortest path to the functionality people would need.

georgiosd wrote Aug 8, 2013 at 10:52 PM

It's all good - it works as expected and it doesn't take much more than doing it the traditional way. Thanks for being so responsive to user requests!

v3nom wrote Aug 9, 2013 at 10:12 AM

I like class.this, but not introducing new things is a strong point. Current solution was actually what I first tried to do when I saw that TS has ()=>{}. I guess I am not the only one and it should be pretty intuitive for everyone.