Scrieți „this” în interiorul unei metode de clasă (Programare, Jquery, Acest, Typescript, Clase Proxy)

Clark a intrebat.

Știu că acest lucru este, probabil, dureros de simplu, dar am un timp greu de înfășurat capul meu în jurul valorii de ea.

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

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

}

Se pare că am nevoie de un proxy, deci să spunem că folosesc Jquery

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

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

}

Dar, venind de la un fond Actionscript 3, nu sunt foarte sigur ce se întâmplă aici. Îmi pare rău, dar nu sunt sigur unde începe Javascript și unde se termină TypeScript.

updateProxy: () => void

Și, de asemenea, nu sunt convins că fac acest lucru corect. Ultimul lucru pe care mi-l doresc este ca cea mai mare parte a clasei mele să aibă o funcție a() care trebuie să fie accesată cu aProxy() deoarece am impresia că scriu același lucru de două ori? Este normal?

Comentarii

7 răspunsuri
basarat

Dacă doriți this capturat modul TypeScript de a face acest lucru este prin intermediul funcțiilor săgeată. Pentru a-l cita pe Anders:

The this în funcțiile săgeată este încadrat lexical

Iată modul în care îmi place să folosesc acest lucru în avantajul meu:

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

Vezi asta în TypeScript Playground

Puteți vedea că în JavaScript-ul generat this este capturat în afara apelului de funcție:

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

deci this valoarea din interiorul apelului de funcție (care ar putea fi window) nu va fi utilizată.

Pentru a afla mai multe: „Înțelegerea this în TypeScript” (4:05) – YouTube

Comentarii

  • Acest lucru nu este necesar. Ceea ce sugerați este JavaScript idiomatic, dar TypeScript face ca acest lucru să nu fie necesar. –  > Por Tatiana Racheva.
  • @TatianaRacheva utilizarea funcțiilor săgeată în contextul membrilor clasei nu era permisă înainte de TS 0.9.1 (iar acest răspuns a fost înainte de aceasta). Răspuns actualizat la noua sintaxă 🙂 –  > Por basarat.
  • 22

  • TREBUIE SĂ VIZIONAȚI VIDEOCLIPUL – FOARTE UTIL. DOAR 5 MINUTE –  > Por Simon_Weaver.
  • Mulțumesc, @basarat. S-a aprins becul pentru context pentru acest de îndată ce am văzut că ai folosit funcția săgeată la jumătatea videoclipului tău. Vă apreciez. –  > Por Simon.
  • @AaronLS pe terenul de joacă TypeScript pentru a genera var _this = this; trebuie să alegeți ES5; din ES2015 – în interiorul funcțiilor – this este utilizat în locul lui _this –  > Por Stefano Spinucci.
joelnet

Dacă vă scrieți metodele astfel, „this” va fi tratat așa cum vă așteptați.

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

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

O altă opțiune ar fi să legați ‘this’ de apelul funcției:

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

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

Comentarii

  • Din experiența mea, funcția de actualizare este mai bine definită astfel: update = () => { … } –  > Por Shaun Rowan.
  • Am folosit mult typescript și am schimbat de multe ori înainte și înapoi. în prezent, includ acoladele curly braces doar dacă este mai mult decât un simplu apel de metodă. de asemenea, atunci când folosesc typescript + linq (care este dumnezeiesc), formatul este mai frumos. exemplu: Enumerable.From(arr).Where(o => o.id == 123); –  > Por joelnet.
  • Cred că dacă vă uitați la javascript care este generat, veți vedea o diferență semnificativă. Nu este o chestiune de gust. update = () => {} va crea un scoping lexical prin compilarea „var _this = this”, sintaxa dvs. nu. –  > Por Shaun Rowan.
  • S-ar putea să aveți nevoie să vă actualizați biblioteca TypeScript, deoarece aceasta include absolut contextul „_this”. Folosind „() => code()” sau () => { return code(); }” veți obține un cod javascript 100% identic. Iată rezultatul: i.imgur.com/I5J12GE.png. De asemenea, vă puteți convinge singuri prin lipirea codului în typescriptlang.org/Playground –  > Por joelnet.
  • se pare că bind(this) poate fi un lucru rău, deoarece pierde siguranța de tip asupra arginților funcției originale –  > Por robert king.
Simon_Weaver

A se vedea pagina 72 din specificația limbajului typescript https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true

Expresii de funcții săgeată

În exemplul

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

utilizarea unei expresii de funcție săgeată face ca callback-ul să aibă aceeași valoare ca și metoda „start” din jur. Scriind callback-ul ca o expresie de funcție standard, devine necesar să se organizeze manual accesul la metoda „this” din jur, de exemplu prin copierea acesteia într-o variabilă locală:

Acesta este Javascript generat efectiv:

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

Comentarii

  • Din păcate, legătura este ruptă –  > Por Jimmy Kane.
  • @JimmyKane Am actualizat link-ul. În mod ciudat, acest document este vechi de peste un an și încă mai face referire la pagina lor de pornire, dar conținutul important l-am inclus în răspuns. –  > Por Simon_Weaver.
Peter Morris

Problema apare atunci când treceți o funcție ca un callback. Până la momentul în care callback-ul a fost executat, valoarea lui „this” s-ar fi putut schimba în Window, în controlul care invocă callback-ul sau în altceva.

Asigurați-vă că utilizați întotdeauna o expresie lambda în momentul în care treceți o referință la funcția care urmează să fie rechemată. De exemplu

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

Acest lucru se compilează la ceva de genul

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

Deoarece funcția addFile este apelată pe o referință de obiect specifică (_this), aceasta nu utilizează „this” al apelantului, ci valoarea lui _this.

Comentarii

  • Când spuneți la ce se compilează, care dintre ele este cea pe care o arătați? (Instanța săgeată sau cea care doar transmite obiectul metodei?) – (Instanța săgeată sau cea care doar transmite obiectul metodei?)  > Por Vaccano.
  • Lambda. Creați un TS cu acest cod și vedeți ce se compilează. –  > Por Peter Morris.
Karim Ayachi

Foarte târziu la petrecere, dar cred că este foarte important pentru viitorii vizitatori ai acestei întrebări să ia în considerare următoarele:

Celelalte răspunsuri, inclusiv cel acceptat, ratează un punct crucial:

myFunction() { ... }

și

myFunction = () => { ... }

sunt nu același lucru „cu excepția faptului că acesta din urmă capătă this„.

Prima sintaxă creează o metodă pe prototip, în timp ce a doua sintaxă creează o proprietate pe obiect a cărei valoare este o funcție (care, de asemenea, captează this). Puteți vedea acest lucru în mod clar în JavaScript transpus.

Pentru a fi completă:

myFunction = function() { ... }

ar fi la fel ca a doua sintaxă, dar fără captură.

Așadar, folosind sintaxa săgeată în majoritatea cazurilor va rezolva problema legată de legarea la obiect, dar nu este același lucru și există multe situații în care doriți să aveți o funcție adecvată pe prototip în loc de o proprietate.

În aceste cazuri, utilizarea unui proxy sau a unei .bind() de fapt este soluția corectă. (Cu toate că suferă la capitolul lizibilitate.)

Mai multe lecturi aici (nu în principal despre TypeScript, dar principiile rămân valabile):

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

Kenneth

Pe scurt, cuvântul cheie this are întotdeauna o referință la obiectul care a apelat funcția.

În Javascript, din moment ce funcțiile sunt doar variabile, le puteți transmite de la o parte la alta.

Exemplu:

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

Când faci următoarele cu jQuery:

$.proxy(this.update, this);

Ceea ce faceți este să suprascrieți acel context. În spatele scenei, jQuery vă va ghida astfel:

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

Maxter

Ce-ar fi să procedați în felul următor?Declarați o variabilă globală de tip „myClass” și inițializați-o în constructorul clasei:

var _self: myClass;

class myClass {
    classScopeVar: string = "hello";

    constructor() {
        _self = this;
    }

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

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

Notă: Este important să NU folosiți „self”, deoarece acesta are deja o semnificație specială.

Comentarii

  • Problema mare: toate instanțele clasei au același _self, deci nu funcționează. –  > Por Ursul Astra.