Je sais que c'est probablement douloureusement basique, mais j'ai du mal à comprendre.
class Main
{
constructor()
{
requestAnimationFrame(this.update); //fine
}
update(): void
{
requestAnimationFrame(this.update); //error, because this is window
}
}
Il semble que j’ai besoin d’un proxy, disons donc avec Jquery
class Main
{
constructor()
{
this.updateProxy = $.proxy(this.update, this);
requestAnimationFrame(this.updateProxy); //fine
}
updateProxy: () => void
update(): void
{
requestAnimationFrame(this.updateProxy); //fine
}
}
Mais venant d'un contexte Actionscript 3, je ne suis pas vraiment sûr de ce qui se passe ici. Désolé, je ne suis pas sûr de l'endroit où commence Javascript et où se termine TypeScript.
updateProxy: () => void
Et aussi, je ne suis pas convaincu que je le fais bien. La dernière chose que je souhaite, c’est que la plupart des élèves de ma classe aient une fonction a()) à laquelle il faut accéder avec aProxy()
, car j’ai l’impression d’écrire deux fois la même chose. c'est normal?
Si vous voulez capturer this
, la méthode TypeScript consiste à utiliser des fonctions de flèche. Pour citer Anders:
La fonction
this
dans les flèches a une portée lexicale
Voici comment j'aime utiliser ceci à mon avantage:
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;
}
Voir ceci dans le TypeScript Playground
Vous pouvez voir que dans le code JavaScript généré, this
est capturé en dehors de de l'appel de fonction:
var _this = this;
this.prop = 10;
this.func1 = function (arg) {
return arg + " yeah" + _this.prop;
};
de sorte que la valeur this
à l'intérieur de l'appel de fonction (qui pourrait être window
) ne serait pas utilisée.
Pour en savoir plus: “Comprendre this
dans TypeScript” (4:05) - YouTube
Si vous écrivez vos méthodes de la sorte, "ceci" sera traité comme vous le souhaitez.
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));
}
}
Voir page 72 de la spécification du langage TypeScript https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true
Dans l'exemple
class Messenger {
message = "Hello World";
start() {
setTimeout(() => alert(this.message), 3000);
}
};
var messenger = new Messenger();
messenger.start();
l'utilisation d'une expression de fonction de flèche fait que le rappel a la même chose que la méthode ‘start’ environnante. En écrivant le callback en tant qu’expression de fonction standard, il devient nécessaire d’organiser manuellement l’accès à l’environnement environnant, par exemple en le copiant dans une variable locale:
Voici le code Javascript généré:
class Messenger {
message = "Hello World";
start() {
var _this = this;
setTimeout(function() { alert(_this.message); }, 3000);
}
};
Le problème se pose lorsque vous transmettez une fonction en tant que rappel. Au moment où le rappel a été exécuté, la valeur de "this" aurait pu être remplacée par la fenêtre, le contrôle invoquant le rappel ou autre chose.
Assurez-vous de toujours utiliser une expression lambda au moment où vous passez une référence à la fonction à rappeler. Par exemple
public addFile(file) {
this.files.Push(file);
}
//Not like this
someObject.doSomething(addFile);
//but instead, like this
someObject.doSomething( (file) => addFile(file) );
Cela compile à quelque chose comme
this.addFile(file) {
this.files.Push(file);
}
var _this = this;
someObject.doSomething(_this.addFile);
Comme la fonction addFile est appelée sur une référence d'objet spécifique (_this), elle n'utilise pas le "this" de l'appelant, mais la valeur de _this.
En bref, le mot-clé this a toujours une référence à l'objet qui a appelé la fonction.
En Javascript, les fonctions n'étant que des variables, vous pouvez les faire circuler.
Exemple:
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
Lorsque vous effectuez les opérations suivantes avec jQuery:
$.proxy(this.update, this);
Ce que vous faites est de surcharger ce contexte. Dans les coulisses, jQuery vous le garantira:
$.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
}
};
Très tard à la fête, mais je pense qu'il est très important que les futurs visiteurs de cette question tiennent compte de ce qui suit:
Les autres réponses, y compris celle acceptée, passent à côté d'un point crucial:
myFunction() { ... }
et
myFunction = () => { ... }
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 seconde 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.
Donc, en utilisant la syntaxe des flèches dans la plupart des cas résoudra votre problème de liaison à l'objet, mais ce n'est pas la même chose et il y a de nombreuses situations dans lesquelles vous voulez avoir une fonction appropriée sur le prototype au lieu d'une propriété.
Dans ces cas, utiliser un proxy ou .bind()
en fait est la solution correcte. (Bien que souffrant de lisibilité.)
Plus de lecture ici (pas principalement à propos de TypeScript, mais les principes sont valables):
https://ponyfoo.com/articles/binding-methods-to-class-instance-objects