¿Debo escribir métodos como funciones de flecha en la clase de Angular?

En Angular es técnicamente posible escribir métodos de clase como funciones de flecha ES2015, pero nunca he visto a alguien hacerlo. Tome este componente simple, por ejemplo:

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

Esto funciona sin problemas. ¿Hay alguna diferencia? ¿Y por qué debería o no debería usar esto?

Los puntos hechos en esta respuesta de React siguen siendo válidos en Angular, en cualquier otro marco o en JavaScript / TypeScript vainilla.

Los métodos prototipo de clase son ES6, los métodos de flecha de clase no lo son. Los métodos de flecha pertenecen a la propuesta de campos de clase y no a una parte de las especificaciones existentes. Se implementan en TypeScript y también se pueden transcribir con Babel.

En general, es preferible utilizar el method() { ... } prototipo method() { ... } que arrow method = () => { ... } porque es más flexible.

Devolución de llamada

La única oportunidad real que el método de flecha proporciona es que se puede usar sin problemas como una callback:

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

Si el método prototipo se debe utilizar como una callback, se debe vincular adicionalmente, esto se debe hacer preferiblemente en constructor:

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

Un decorador como bind-decorator se puede utilizar en TypeScript y ES Next para proporcionar una alternativa más concisa al enlace de métodos en el constructor:

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

Herencia

El método de flecha restringe las clases secundarias para que también usen métodos de flecha, de lo contrario no se anularán. Esto crea un problema si se pasó por alto una flecha:

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

No es posible usar super.method() en la clase hija porque super.method refiere a Parent.prototype.method , que no existe:

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

Mixins

Los métodos prototipo se pueden usar de manera eficiente en mixins. Mixins es útil para la herencia múltiple o para solucionar problemas en la visibilidad del método TypeScript.

Como el método de flecha no está disponible en el prototipo de clase, no se puede acceder desde fuera de la clase:

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

Pruebas

Una característica valiosa que proporcionan los métodos prototipo es que son accesibles antes de la creación de instancias de clases, por lo que pueden ser espiados o burlados en las pruebas, incluso si se llaman inmediatamente después de la construcción:

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

Esto no es posible cuando un método es una flecha.

TL; DR: la elección entre prototipo y métodos de clase de flecha parece una cuestión de gusto, pero en realidad el uso de métodos prototipo es más previsora. Por lo general, puede querer evitar los métodos de clase de flecha, a menos que esté seguro de que no causarán inconvenientes. No te olvides de usar los métodos de bind en prototipo si los pasas como devoluciones de llamada.

Un buen caso de uso de las funciones de flecha de clase es cuando desea pasar una función a otro componente y guardar el contexto del componente actual en la función.

 @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.() } }  

En el ejemplo anterior, el child puede llamar a la función del componente principal y el texto se actualizará correctamente, donde como si simplemente cambiara un poco el elemento principal:

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

Solo hay un caso en el que debe abstenerse de utilizar las funciones de flecha si necesita hacer una comstackción de AOT, como se documenta aquí

Al configurar un módulo, no puede usar las funciones de flecha.

❌ NO:

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

✅ HACER:

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