105 votes

Typescript "this" à l'intérieur d'une méthode de classe

Je sais que c'est probablement très basique, mais j'ai du mal à comprendre.

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

     update(): void
     {
         requestAnimationFrame(this.update);  //erreur, car this fait référence à window
     }

}

Il semble que j'ai besoin d'un proxy, donc disons en utilisant Jquery

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

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

}

Mais venant d'un environnement Actionscript 3, je ne suis pas vraiment sûr de ce qui se passe ici. Désolé, je ne suis pas sûr où commence JavaScript et où se termine TypeScript.

updateProxy: () => void

Et aussi, je ne suis pas convaincu que je fais les choses correctement. La dernière chose que je veux, c'est que la plupart de ma classe ait une fonction a() qui doit être utilisée avec aProxy() car j'ai l'impression d'écrire la même chose deux fois. Est-ce normal?

0 votes

J'ai trouvé cette documentation très utile github.com/Microsoft/TypeScript/wiki/…

145voto

basarat Points 22425

Si vous voulez this capturé le TypeScript procède de cette manière via les fonctions à flèche. Pour citer Anders:

Le this dans les fonctions à flèche est à portée lexicale

Voici la manière dont j'aime utiliser ceci à mon avantage:

class test{
    // Utiliser des fonctions à flèche
    func1=(arg:string)=>{
            return arg+" ouais" + this.prop;
    }
    func2=(arg:number)=>{
            return arg+10 + this.prop;
    }       

    // une propriété sur this
    prop = 10;      
}

Visualiser ceci dans le TypeScript Playground

Vous pouvez voir que dans le JavaScript généré, this est capturé en dehors de l'appel de la fonction:

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

donc la valeur du this à l'intérieur de l'appel de la fonction (qui pourrait être window) ne serait pas utilisée.

Pour en savoir plus: "Comprendre this en TypeScript" (4:05) - YouTube

2 votes

Il n'est pas nécessaire. Ce que vous suggérez est du JavaScript idiomatique, mais TypeScript rend cela inutile.

1 votes

@TatianaRacheva l'utilisation de fonctions fléchées dans le contexte des membres de classe n'était pas autorisée avant TS 0.9.1 (et cette réponse était avant cela). Réponse mise à jour vers la nouvelle syntaxe :)

0 votes

Merci d'avoir édité, puisque la réponse est choisie par l'OP, j'étais confus par cela.

24voto

joelnet Points 1762

Si vous écrivez vos méthodes de cette manière, 'this' sera traité comme vous vous y attendez.

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

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

Une autre option serait de lier 'this' à l'appel de fonction :

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

    update(): void
    {
        requestAnimationFrame(this.update.bind(this));
    }
}

2 votes

Dans mon expérience la fonction de mise à jour est mieux définie comme suit : miseAjour = () => { ... }

0 votes

Je suis souvent passé de TypeScript et Python, j'ai changé en allant et en venant plusieurs fois. en ce moment, je n'inclus que les accolades si c'est plus qu'un simple appel de méthode. aussi quand on utilise TypeScript + linq (qui est divin), le format est plus agréable. exemple: Enumerable.From(arr).Where(o => o.id == 123);

0 votes

Je pense que si vous regardez le javascript qui est généré, vous verrez une différence significative. Ce n'est pas une question de goût. update = () => {} créera une portée lexicale via la compilation "var _this = this", votre syntaxe ne le fera pas.

8voto

Simon_Weaver Points 31141

Consultez la page 72 de la spécification du langage TypeScript https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true

Expressions de fonctions fléchées

Dans l'exemple

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

l'utilisation d'une expression de fonction fléchée fait en sorte que le rappel ait le même this que la méthode environnante 'start'. En écrivant le rappel en tant qu'expression de fonction standard, il devient nécessaire d'accéder manuellement au this environnant, par exemple en le copiant dans une variable locale :

Voici le Javascript généré :

class Messenger {
 message = "Bonjour le monde";
 start() {
 var _this = this;
 setTimeout(function() { alert(_this.message); }, 3000);
 }
};

2 votes

Le lien est malheureusement cassé

1 votes

@JimmyKane J'ai mis à jour le lien. Étrangement, ce document a plus d'un an et est toujours référencé sur leur page d'accueil, mais le contenu important que j'ai inclus se trouve dans la réponse.

8voto

Karim Ayachi Points 92

Très en retard à la fête, mais je pense qu'il est très important pour les futurs visiteurs de cette question de considérer ce qui suit :

Les autres réponses, y compris celle acceptée, omettent un point crucial :

myFunction() { ... }

et

myFunction = () => { ... }

ne sont pas la même chose "à l'exception que ce dernier capture this".

La première syntaxe crée une méthode sur le prototype, tandis que la deuxième syntaxe crée une propriété sur l'objet dont la valeur est une fonction (qui capture également this). Vous pouvez le voir clairement dans le JavaScript transpilé.

Pour être complet :

myFunction = function() { ... }

serait la même chose que la deuxième syntaxe, mais sans la capture.

Ainsi, l'utilisation de la syntaxe de la flèche dans la plupart des cas résoudra votre problème de liaison avec l'objet, mais ce n'est pas la même chose et il existe de nombreuses situations où vous voulez avoir une fonction propre sur le prototype plutôt qu'une propriété.

Dans ces cas, l'utilisation d'un proxy ou de .bind() est en fait la solution correcte. (Bien que cela compromette la lisibilité.)

Plus de lecture ici (pas principalement sur TypeScript, mais les principes restent les mêmes) :

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

5voto

Peter Morris Points 2402

Le problème survient lorsque vous transmettez une fonction en tant que rappel. À ce moment-là, une fois que le rappel a été exécuté, la valeur de "this" pourrait avoir changé pour être la fenêtre, le contrôle appelant le rappel, ou autre chose.

Assurez-vous d'utiliser toujours une expression lambda au moment où vous transmettez une référence à la fonction à rappeler. Par exemple

public addFile(file) {
  this.files.push(file);
}
//Pas comme ceci
someObject.doSomething(addFile);
//mais plutôt comme ça
someObject.doSomething( (file) => addFile(file) );

Cela se compile en quelque chose comme

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

Parce que la fonction addFile est appelée sur une référence d'objet spécifique (_this), elle n'utilise pas le "this" de l'appeleur mais plutôt la valeur de _this.

0 votes

Lorsque vous dites à quoi cela compile, lequel montrez-vous? (L'instance de flèche ou celle qui passe simplement l'objet de méthode?)

0 votes

Le lambda. Créez simplement un TS avec ce code et regardez ce qu'il compile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X