Devo scrivere metodi come funzioni di freccia nella class di Angular

In Angular è tecnicamente ansible scrivere metodi di class come funzioni freccia ES2015, ma non ho mai visto qualcuno farlo. Prendi questo semplice componente per esempio:

@Component({ selector: 'sample' }) export class SampleComponent { arrowFunction = param => { // Do something }; normalFunction(param) { // Do something } } 

Funziona senza problemi. Ci sono delle differenze? E perché dovrei o non dovrei usarlo?

I punti formulati in questa risposta React sono ancora validi in Angular, qualsiasi altro framework o vanilla JavaScript / TypeScript.

I metodi di prototipo di class sono ES6, i metodi di class arrow non lo sono. I metodi freccia appartengono alla proposta dei campi di class e non fanno parte delle specifiche esistenti. Sono implementati in TypeScript e possono essere transpiled anche con Babel.

In genere è preferibile utilizzare il method() { ... } prototype method() { ... } di arrow method = () => { ... } perché è più flessibile.

callback

L’unica vera opportunità offerta dal metodo freccia è che può essere utilizzata senza problemi come callback:

 class Class { method = () => { ... } } registerCallback(new Class().method); 

Se il metodo prototype dovrebbe essere usato come callback, dovrebbe essere anche associato, questo dovrebbe essere preferibilmente fatto nel costruttore:

 class Class { constructor() { this.method = this.method.bind(this); } method() { ... } } registerCallback(new Class().method); 

Un decoratore come bind-decorator può essere utilizzato in TypeScript ed ES Next per fornire un’alternativa più concisa al binding del metodo nel costruttore:

 import bind from 'bind-decorator'; class Class { @bind method() { ... } } 

Eredità

Il metodo Arrow limita le classi figlie ad usare anche i metodi freccia, altrimenti non saranno sovrascritte. Questo crea un problema se una freccia è stata trascurata:

 class Parent { method = () => { ... } } class Child extends Parent { method() { ... } // won't override Parent method } 

Non è ansible utilizzare super.method() in class figlio perché super.method fa riferimento a Parent.prototype.method , che non esiste:

 class Parent { method = () => { ... } } class Child extends Parent { method = () => { super.method(); // won't work ... } } 

mixins

I metodi di prototipazione possono essere utilizzati in modo efficiente nei mix. I mix sono utili per l’ereditarietà multipla o per correggere i problemi nella visibilità del metodo TypeScript.

Poiché il metodo arrow non è disponibile sul prototipo di class, non può essere raggiunto dall’esterno della class:

 class Parent { method = () => { ... } } class Child extends OtherParent { ... } Object.assign(Child.prototype, Parent.prototype) // method won't be copied 

analisi

Una preziosa funzionalità fornita dai prototipi è che sono accessibili prima dell’istanza della class, quindi possono essere spiati o derisi nei test, anche se vengono chiamati subito dopo la costruzione:

 class Class { constructor(arg) { this.init(arg); } init(arg) { ... } } spyOn(Class.prototype, 'init').and.callThrough(); const object = new Class(1); expect(object.init).toHaveBeenCalledWith(1); 

Questo non è ansible quando un metodo è una freccia.

TL; DR: la scelta tra metodi di prototipo e class di frecce sembra una questione di gusti, ma in realtà l’uso di metodi prototipo è più lungimirante. Di solito, potresti voler evitare i metodi di class della freccia, a meno che tu non sia sicuro che non causeranno alcun inconveniente. Non dimenticare di utilizzare il bind sui metodi prototipo se li passi come richiami.

Un buon utilizzo delle funzioni freccia di class si ha quando si desidera passare una funzione a un altro componente e salvare il contesto del componente corrente nella funzione.

 @Component({ template:` I'm the parent  ` }) export class PerentComponent{ text= "default text" arrowFunction = param => { // Do something // let's update something in parent component ( this) this.text = "Updated by parent, but called by child" }; } @Component({ template:` I'm the child component ` }) export class ChildComponent{ @Input() parentFunction; ngOnInit(){ this.parentFunction.() } }  

Nell’esempio sopra, il child è in grado di chiamare la funzione del componente genitore e il testo verrà aggiornato correttamente, dove come se io modificassi un po ‘il genitore per essere:

 export class PerentComponent{ text= "default text" arrowFunction (){ this.text = "This text will never update the parent's text property, because `this` will be child component " }; } 

C’è solo un caso in cui è necessario astenersi dall’utilizzare le funzioni di freccia se è necessario eseguire la compilazione AOT, come documentato qui

Quando si configura un modulo, non è ansible utilizzare le funzioni freccia.

❌ DONT:

 import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { Routes, RouterModule } from '@angular/router'; @NgModule({ imports: [ BrowserModule, RouterModule, HttpModule, RouterModule.forRoot([], { errorHandler: (err) => console.error(err) }) ], bootstrap: [ AppComponent ], declarations: [ AppComponent ] }) export class AppModule {} 

✅ DO:

 import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { Routes, RouterModule } from '@angular/router'; function errorHandler(err) { console.error(err); } @NgModule({ imports: [ BrowserModule, RouterModule, HttpModule, RouterModule.forRoot([], { errorHandler }) ], bootstrap: [ AppComponent ], declarations: [ AppComponent ] }) export class AppModule {}