javascript - Care este diferența dintre metoda unei clase, funcția câmpului unei clase și funcția săgeată a câmpului unei clase?
Care este diferența dintre o metodă de clasă, o proprietate de clasă care este o funcție și o proprietate de clasă care este o funcție săgeată? Se comportă diferit cuvântul cheie this în diferitele variante ale metodei?
class Greeter {
constructor() {
this.greet();
this.greet2();
this.greet3();
}
greet() {
console.log('greet1', this);
}
greet2 = () => {
console.log('greet2', this);
}
greet3 = function() {
console.log('greet3', this);
}
}
let bla = new Greeter();
Acesta este JavaScript-ul rezultat atunci când este transpilat din TypeScript.
var Greeter = /** @class */ (function () {
function Greeter() {
var _this = this;
this.greet2 = function () {
console.log('greet2', _this);
};
this.greet3 = function () {
console.log('greet3', this);
};
this.greet();
this.greet2();
this.greet3();
}
Greeter.prototype.greet = function () {
console.log('greet1', this);
};
return Greeter;
}());
var bla = new Greeter();
Versiunea mea de TypeScript este 3.4.5.
Răspuns #1
Există diferențe între toate cele 3 versiuni. Aceste diferențe se află în 3 domenii:
- Cine este
thisla rulare - Unde este atribuită funcția
- Care este tipul lui
thisîn TypeScript.
Să începem cu locurile unde funcționează la fel. Luați în considerare această clasă, cu un câmp de clasă:
class Greeter {
constructor(private x: string) {
}
greet() {
console.log('greet1', this.x);
}
greet2 = () => {
console.log('greet2', this.x);
}
greet3 = function () {
// this is typed as any
console.log('greet3', this.x);
}
}
let bla = new Greeter(" me");
Cu această clasă, toate cele 3 apeluri de funcții vor afișa așa cum se așteaptă: 'greet* me' atunci când sunt invocate pe bla
bla.greet()
bla.greet2()
bla.greet3()
Cine este acesta la rulare
Funcțiile săgeată capturează this din contextul declarației, așa că this din greet2 este întotdeauna garantat să fie instanța clasei care a creat această funcție. Celelalte versiuni (metoda și funcția) nu oferă astfel de garanții.
Deci, în acest cod nu toate trei afișează același text:
function call(fn: () => void) {
fn();
}
call(bla.greet) // greet1 undefined
call(bla.greet2) //greet2 me
call(bla.greet3) // greet3 undefined
Acest lucru este deosebit de important atunci când transmiți funcția ca handler de eveniment către un alt component.
Unde este atribuită funcția
Metodele clasei (cum ar fi greet) sunt atribuite pe prototip, inițializările câmpurilor (cum ar fi greet2 și greet3) sunt atribuite în constructor. Aceasta înseamnă că greet2 și greet3 vor avea o amprentă de memorie mai mare, deoarece necesită alocarea unui nou closure de fiecare dată când Greeter este instanțiat.
Care este tipul acestuia în TypeScript.
Typescript va tipiza this ca o instanță a Greeter atât în metoda (greet), cât și în funcția săgeată (greet2), dar va tipiza this ca any în greet3. Acest lucru va genera o eroare dacă încerci să utilizezi this în greet3 sub noImplicitAny
Când să le folosești
Folosește sintaxa metodei dacă această funcție nu va fi transmisă ca handler de eveniment către un alt component (decât dacă folosești
bindsau altceva pentru a te asigura căthisrămâne instanța clasei)Folosește sintaxa funcțiilor săgeată atunci când funcția ta va fi transmisă altor componente și ai nevoie de acces la
thisîn interiorul funcției.Nu pot să mă gândesc cu adevărat la un caz de utilizare bun pentru asta, în general evit.
Răspunsul #2
Diferența cuvântului cheie this:
În cele de mai sus, toate trei au același this, dar vei vedea diferența când vei transmite metoda către alte funcții.
class Greeter {
constructor() {
}
greet() {
console.log(this);
}
greet2 = () => {
console.log(this);
}
greet3 = function() {
console.log(this);
}
}
let bla = new Greeter();
function wrapper(f){
f();
}
wrapper(bla.greet) //undefined
wrapper(bla.greet2) //Greeter
wrapper(bla.greet3) //undefinedDar există o altă diferență: prima metodă este pe prototype al class, în timp ce celelalte două nu sunt. Ele sunt metode ale instanței obiectului.
class Greeter {
constructor() {
}
greet() {
console.log('greet1', this);
}
greet2 = () => {
console.log('greet2', this);
}
greet3 = function() {
console.log('greet3', this);
}
}
let bla = new Greeter();
console.log(Object.getOwnPropertyNames(Greeter.prototype))Dacă am în clasă ->
str="my string";și în toate cele 3 metode pot spuneconsole.log(this.str)și afișează „my string”. Dar mă întreb - este acesta chiar același lucru
Nu, ele nu sunt același lucru. Așa cum am menționat, greet2 și greet3 nu vor fi pe Greeter.prototype, ci vor fi pe instanța însăși. Aceasta înseamnă că dacă creezi 1000 de instanțe de Greeter, vor exista 1000 de metode diferite (greet2 și greet3) stocate în memorie pentru cele 1000 de instanțe diferite. Dar va exista o singură metodă greet pentru toate instanțele.
Vezi fragmentul de mai jos cu două instanțe de Greeter()
