105

I know this is probably painfully basic, but i am having a tough time wrapping my head around it.

class Main
{
     constructor()
     {
         requestAnimationFrame(this.update);  //fine    
     }

     update(): void
     {
         requestAnimationFrame(this.update);  //error, because this is window
     }

}

It appears to be the case that I need a proxy, so lets say using Jquery

class Main
{
     constructor()
     {
         this.updateProxy = $.proxy(this.update, this);
         requestAnimationFrame(this.updateProxy);  //fine    
     }

     updateProxy: () => void
     update(): void
     {
         requestAnimationFrame(this.updateProxy);  //fine
     }

}

But coming from an Actionscript 3 background, I am not really sure what is happening here. Sorry I am not sure where Javascript begins and TypeScript ends.

updateProxy: () => void

And also, I am not convinced I am doing this right. The last thing I want is most of my class having a a() function which needs to be accessed with aProxy() as I feel I am writing the same thing twice? Is it normal?

iberbeu
  • 15,295
  • 5
  • 27
  • 48
Clark
  • 2,598
  • 3
  • 27
  • 41

7 Answers7

145

If you want this captured the TypeScript way of doing this is via arrow functions. To quote Anders:

The this in arrow functions is lexically scoped

Here is the way I like to use this to my advantage:

class test{
    // Use arrow functions
    func1=(arg:string)=>{
            return arg+" yeah" + this.prop;
    }
    func2=(arg:number)=>{
            return arg+10 + this.prop;
    }       

    // some property on this
    prop = 10;      
}

View this in the TypeScript Playground

You can see that in the generated JavaScript this is captured outside the function call:

var _this = this;
this.prop = 10;
this.func1 = function (arg) {
    return arg + " yeah" + _this.prop;
};

so the this value inside the function call (which could be window) would not be used.

To learn more: “Understanding this in TypeScript” (4:05) – YouTube

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
basarat
  • 261,912
  • 58
  • 460
  • 511
  • 2
    This is not necessary. What you're suggesting is idiomatic JavaScript, but TypeScript makes this unnecessary. – Tatiana Racheva Dec 15 '13 at 21:44
  • 1
    @TatianaRacheva using arrow functions in the context of class members was not allowed before TS 0.9.1 (and this answer was before that). Updated answer to new syntax :) – basarat Dec 15 '13 at 22:28
  • thanks for editing, since the answer is chosen by the OP, I was confused by it. – Tatiana Racheva Dec 31 '13 at 21:51
  • 37
    YOU MUST WATCH THE VIDEO - VERY USEFUL. ONLY 5 MINUTES – Simon_Weaver May 22 '14 at 20:32
  • 1
    Thank you, @basarat. Light-bulb went on for context for *this* as soon as I saw you use the arrow function half-way through your video. I appreciate you. – Simon Aug 14 '15 at 11:45
  • I was bashing my head, and i was aware of context problems, but not this use of arrow functions, thanks! – Marco Feb 27 '19 at 20:44
  • @basarat Nice one Bas! I can't believe that I'd forgotten this and that I've circled back around to this 6 years later to find your answer again. I owe you a handroll ;-) – Scott Mar 10 '19 at 04:25
  • I'm reviewing the linked generated script, and it no longer generates a `var _this = this;`, why is that? – AaronLS Jan 12 '20 at 06:29
  • 1
    @AaronLS on TypeScript playground to generate `var _this = this;` you have to choose `ES5`; since ES2015 - inside functions - `this` is used in place of `_this` – Stefano Spinucci Oct 01 '20 at 21:29
24

If you write your methods like this, 'this' will be treated the way you expect.

class Main
{
    constructor()
    {
        requestAnimationFrame(() => this.update());
    }

    update(): void
    {
        requestAnimationFrame(() => this.update());
    }
}

Another option would be to bind 'this' to the function call:

class Main
{
    constructor()
    {
        requestAnimationFrame(this.update.bind(this));
    }

    update(): void
    {
        requestAnimationFrame(this.update.bind(this));
    }
}
joelnet
  • 13,621
  • 5
  • 35
  • 49
  • 2
    In my experience the update function is better defined like this: update = () => { ... } – Shaun Rowan Apr 02 '14 at 18:33
  • I've been using typescript a lot and have changed back and forth many times. at the moment, i only include the curly braces if it's more than a simple method call. also when using typescript + linq (which is godly), the format is nicer. example: Enumerable.From(arr).Where(o => o.id == 123); – joelnet Apr 03 '14 at 23:18
  • I think if you look at the javascript which is generated, you will see a significant difference. It is not a matter of taste. update = () => {} will create lexical scoping via "var _this = this" compilation, your syntax will not. – Shaun Rowan Apr 04 '14 at 17:34
  • 1
    You might need to upgrade your TypeScript library because it absolutely does include the "_this" context. Using "() => code()" or () => { return code(); }" will output 100% identical javascript code. Here's the output: http://i.imgur.com/I5J12GE.png. You can also see for yourself by pasting the code into http://www.typescriptlang.org/Playground – joelnet Apr 05 '14 at 19:08
  • apparently bind(this) can be bad because it looses type safety on the original function args – Rusty Rob Jul 12 '17 at 01:29
8

See page 72 of the typescript language specification https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true

Arrow Function Expressions

In the example

class Messenger {
 message = "Hello World";
 start() {
 setTimeout(() => alert(this.message), 3000);
 }
};
var messenger = new Messenger();
messenger.start();

the use of an arrow function expression causes the callback to have the same this as the surrounding ‘start’ method. Writing the callback as a standard function expression it becomes necessary to manually arrange access to the surrounding this, for example by copying it into a local variable:

This is the actual generated Javascript:

class Messenger {
 message = "Hello World";
 start() {
 var _this = this;
 setTimeout(function() { alert(_this.message); }, 3000);
 }
};
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
8

Very late to the party, but I think it is very important for future visitors of this question to consider the following:

The other answers, including the accepted one, miss a crucial point:

myFunction() { ... }

and

myFunction = () => { ... }

are not the same thing "with the exception that the latter captures this".

The first syntax creates a method on the prototype, while the second syntax creates a property on the object whos value is a function (that also happens to capture this). You can see this clearly in the transpiled JavaScript.

To be complete:

myFunction = function() { ... }

would be the same als the second syntax, but without the capturing.

So, using the arrow syntax in most cases will fix your problem of binding to the object, but it's not the same and there are many situations where you do want to have a proper function on the prototype in stead of a property.

In these cases using a proxy or .bind() actually is the correct solution. (Although suffering readability.)

More reading here (not primarily about TypeScript, but the principles stand):

https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1

https://ponyfoo.com/articles/binding-methods-to-class-instance-objects

Karim Ayachi
  • 547
  • 5
  • 11
5

The problem arises when you pass a function as a callback. By the time the callback has executed the value of "this" could have changed to the Window, the control invoking the callback, or something else.

Make sure you always use a lambda expression at the point you pass a reference to the function to be called back. For example

public addFile(file) {
  this.files.push(file);
}
//Not like this
someObject.doSomething(addFile);
//but instead, like this
someObject.doSomething( (file) => addFile(file) );

This compiles to something like

this.addFile(file) {
  this.files.push(file);
}
var _this = this;
someObject.doSomething(_this.addFile);

Because the addFile function is being called on a specific object reference (_this) it does not use the "this" of the invoker but instead the value of _this.

Peter Morris
  • 20,174
  • 9
  • 81
  • 146
  • When you say what it compiles to, which one are you showing? (The arrow instance or the the one that just passes the method object?) – Vaccano Nov 14 '15 at 15:29
  • The lambda. Just create a TS with that code and take a look at what it compiles. – Peter Morris Nov 14 '15 at 20:01
2

In short, the this keyword always has a reference to the object that called the function.

In Javascript, since functions are just variables, you can pass them around.

Example:

var x = {
   localvar: 5, 
   test: function(){
      alert(this.localvar);
   }
};

x.test() // outputs 5

var y;
y.somemethod = x.test; // assign the function test from x to the 'property' somemethod on y
y.test();              // outputs undefined, this now points to y and y has no localvar

y.localvar = "super dooper string";
y.test();              // outputs super dooper string

When you do the following with jQuery:

$.proxy(this.update, this);

What you are doing is overriding that context. Behind the scenes jQuery will guive you this:

$.proxy = function(fnc, scope){
  return function(){
     return fnc.apply(scope);  // apply is a method on a function that calls that function with a given this value
  }
};
Kenneth
  • 28,294
  • 6
  • 61
  • 84
2

How about doing it this way? Declare a global variable of type "myClass" and initialise it in the constructor of the class:

var _self: myClass;

class myClass {
    classScopeVar: string = "hello";

    constructor() {
        _self = this;
    }

    alerter() {
        setTimeout(function () {
            alert(_self.classScopeVar)
        }, 500);
    }
}

var classInstance = new myClass();
classInstance.alerter();

Note: It is important NOT to use "self" as it as a special meaning already.

Maxter
  • 716
  • 7
  • 15