Typoskript "this" innerhalb einer Klassenmethode

81

Ich weiß, dass dies wahrscheinlich schmerzhaft einfach ist, aber es fällt mir schwer, meinen Kopf darum zu wickeln.

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

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

}

Es scheint der Fall zu sein, dass ich einen Proxy benötige, also verwenden wir Jquery

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

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

}

Ich komme jedoch aus Actionscript 3 und bin mir nicht sicher, was hier passiert. Entschuldigung, ich bin nicht sicher, wo Javascript beginnt und TypeScript endet.

updateProxy: () => void

Und ich bin auch nicht davon überzeugt, dass ich das richtig mache. Das Letzte, was ich möchte, ist, dass der größte Teil meiner Klasse eine aa () -Funktion hat, auf die zugegriffen werden muss, aProxy()da ich das Gefühl habe, zweimal dasselbe zu schreiben? Es ist normal?

Clark
quelle
Ich fand diese Dokumentation sehr hilfreich github.com/Microsoft/TypeScript/wiki/…
rainversion_3

Antworten:

116

Wenn Sie thisdie TypeScript-Methode erfassen möchten , erfolgt dies über Pfeilfunktionen. Um Anders zu zitieren:

Die thisIn-Pfeil-Funktionen sind lexikalisch festgelegt

Hier ist die Art und Weise, wie ich dies zu meinem Vorteil nutzen möchte:

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;      
}

Zeigen Sie dies auf dem TypeScript-Spielplatz an

Sie können sehen, dass im generierten JavaScript außerhalb des Funktionsaufrufs thiserfasst wird :

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

Daher wird der thisWert innerhalb des Funktionsaufrufs (der sein könnte window) nicht verwendet.

Weitere Informationen : thisIn TypeScript verstehen “ (4:05) - YouTube

Basarat
quelle
2
Dies ist nicht erforderlich. Was Sie vorschlagen, ist idiomatisches JavaScript, aber TypeScript macht dies unnötig.
Tatiana Racheva
1
@TatianaRacheva mit Pfeilfunktionen im Kontext von Klassenmitgliedern war vor TS 0.9.1 nicht zulässig (und diese Antwort war davor). Aktualisierte Antwort auf neue Syntax :)
Basarat
18
SIE MÜSSEN DAS VIDEO ANSEHEN - SEHR NÜTZLICH. NUR 5
MINUTEN
1
Vielen Dank, @basarat. Die Glühbirne ging für den Kontext dazu an, sobald ich sah, dass Sie die Pfeilfunktion in der Mitte Ihres Videos verwenden. Ich schätze Sie.
Simon
1
@AaronLS auf dem TypeScript-Spielplatz zum Generieren müssen var _this = this;Sie auswählen ES5; da ES2015 - Innenfunktionen - thisanstelle von_this
Stefano Spinucci
18

Wenn Sie Ihre Methoden so schreiben, wird "dies" so behandelt, wie Sie es erwarten.

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

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

Eine andere Möglichkeit wäre, 'this' an den Funktionsaufruf zu binden:

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

    update(): void
    {
        requestAnimationFrame(this.update.bind(this));
    }
}
Joelnet
quelle
2
Nach meiner Erfahrung ist die Update-Funktion besser wie folgt definiert: update = () => {...}
Shaun Rowan
Ich habe viel Typoskript verwendet und mich viele Male hin und her gewechselt. Im Moment schließe ich die geschweiften Klammern nur ein, wenn es mehr als ein einfacher Methodenaufruf ist. Auch bei der Verwendung von Typoskript + Linq (was göttlich ist) ist das Format besser. Beispiel: Enumerable.From (arr) .Where (o => o.id == 123);
Joelnet
Ich denke, wenn Sie sich das generierte Javascript ansehen, werden Sie einen signifikanten Unterschied feststellen. Es ist keine Geschmackssache. update = () => {} erstellt ein lexikalisches Scoping über die Kompilierung "var _this = this", Ihre Syntax jedoch nicht.
Shaun Rowan
1
Möglicherweise müssen Sie Ihre TypeScript-Bibliothek aktualisieren, da sie unbedingt den Kontext "_this" enthält. Verwenden von "() => code ()" oder () => {return code (); .}“Ausgegeben zu 100% identisch JavaScript - Code Hier ist der Ausgang: i.imgur.com/I5J12GE.png Sie können durch Einfügen der Code in sich selbst auch. Typescriptlang.org/Playground
joelnet
anscheinend kann bind (this) schlecht sein, weil es die Typensicherheit für die ursprüngliche Funktion args verliert
robert king
6

Siehe Seite 72 der Typenskript-Sprachspezifikation https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true

Pfeilfunktionsausdrücke

Im Beispiel

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

Die Verwendung eines Pfeilfunktionsausdrucks bewirkt, dass der Rückruf dieselbe Funktion wie die umgebende Startmethode hat. Wenn Sie den Rückruf als Standardfunktionsausdruck schreiben, müssen Sie den Zugriff auf die Umgebung manuell arrangieren, indem Sie ihn beispielsweise in eine lokale Variable kopieren:

Dies ist das tatsächlich generierte Javascript:

class Messenger {
 message = "Hello World";
 start() {
 var _this = this;
 setTimeout(function() { alert(_this.message); }, 3000);
 }
};
Simon_Weaver
quelle
Link ist leider kaputt
Jimmy Kane
1
@ JimmyKane Ich habe den Link aktualisiert. Seltsamerweise ist dieses Dokument über ein Jahr alt und wird immer noch von der Homepage referenziert, aber der wichtige Inhalt, den ich in die Antwort aufgenommen habe.
Simon_Weaver
4

Das Problem tritt auf, wenn Sie eine Funktion als Rückruf übergeben. Bis der Rückruf ausgeführt wurde, könnte sich der Wert von "this" in das Fenster, das Steuerelement, das den Rückruf aufruft, oder etwas anderes geändert haben.

Stellen Sie sicher, dass Sie an der Stelle, an der Sie einen Verweis auf die zurückzurufende Funktion übergeben, immer einen Lambda-Ausdruck verwenden. Zum Beispiel

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

Dies kompiliert zu so etwas wie

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

Da die Funktion addFile für eine bestimmte Objektreferenz (_this) aufgerufen wird, wird nicht das "this" des Aufrufers verwendet, sondern der Wert von _this.

Peter Morris
quelle
Wenn Sie sagen, woraus es kompiliert wird, welches zeigen Sie? (Die
Pfeilinstanz
Das Lambda. Erstellen Sie einfach einen TS mit diesem Code und sehen Sie sich an, was er kompiliert.
Peter Morris
2

Kurz gesagt, das Schlüsselwort this hat immer einen Verweis auf das Objekt, das die Funktion aufgerufen hat.

Da Funktionen in Javascript nur Variablen sind, können Sie sie weitergeben.

Beispiel:

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

Wenn Sie mit jQuery Folgendes tun:

$.proxy(this.update, this);

Was Sie tun, ist, diesen Kontext zu überschreiben. Hinter den Kulissen wird Ihnen jQuery Folgendes empfehlen:

$.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
quelle
2

Sehr spät zur Party, aber ich denke, es ist sehr wichtig, dass zukünftige Besucher dieser Frage Folgendes berücksichtigen:

Die anderen Antworten, einschließlich der akzeptierten, verfehlen einen entscheidenden Punkt:

myFunction() { ... }

und

myFunction = () => { ... }

sind nicht dasselbe "mit der Ausnahme, dass letztere erfasst this".

Die erste Syntax erstellt eine Methode für den Prototyp, während die zweite Syntax eine Eigenschaft für das Objekt erstellt, dessen Wert eine Funktion ist (die auch erfasst wird this). Sie können dies deutlich im transpilierten JavaScript sehen.

Vollständig sein:

myFunction = function() { ... }

wäre das gleiche wie die zweite Syntax, aber ohne die Erfassung.

Die Verwendung der Pfeilsyntax behebt in den meisten Fällen das Problem der Bindung an das Objekt. Es ist jedoch nicht dasselbe, und es gibt viele Situationen, in denen Sie anstelle einer Eigenschaft eine ordnungsgemäße Funktion für den Prototyp wünschen.

In diesen Fällen einen Proxy verwenden oder .bind()tatsächlich ist die richtige Lösung. (Obwohl unter Lesbarkeit leiden.)

Lesen Sie hier mehr (nicht in erster Linie über TypeScript, aber die Prinzipien stehen):

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
quelle
0

Wie wäre es so? Deklarieren Sie eine globale Variable vom Typ "myClass" und initialisieren Sie sie im Konstruktor der Klasse:

var _self: myClass;

class myClass {
    classScopeVar: string = "hello";

    constructor() {
        _self = this;
    }

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

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

Hinweis: Es ist wichtig, "Selbst" NICHT als spezielle Bedeutung zu verwenden.

Maxter
quelle