我应该在 Angular 的类中将方法写成箭头函数吗

Posted

技术标签:

【中文标题】我应该在 Angular 的类中将方法写成箭头函数吗【英文标题】:Should I write methods as arrow functions in Angular's class 【发布时间】:2018-02-03 12:28:45 【问题描述】:

在 Angular 中,技术上可以将类方法编写为 ES2015 箭头函数,但我从未真正见过有人这样做。以这个简单的组件为例:

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

这没有任何问题。有什么不同吗?我为什么应该或不应该使用它?

【问题讨论】:

【参考方案1】:

类箭头函数的一个很好的用例是当你想将一个函数传递给另一个组件并在函数中保存当前组件的上下文时。

@Component(

   template:`
        I'm the parent
       <child-component></child-component>

  `
)
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.()
   


 <parent-component></parent-component>

在上面的例子中,child 能够调用父组件的函数并且文本将正确更新,就像我只是将父组件更改为:

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

【讨论】:

【参考方案2】:

只有一种情况是,如果您需要进行 AOT 编译,则必须避免使用箭头函数,如文档所述 here

配置模块时,不能使用箭头函数。

❌不要:

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 

✅ 做:

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 

【讨论】:

正如文章中提到的,这似乎只适用于配置模块的过程中,这表明箭头函数作为类方法对于 AOT 来说是可以的。【参考方案3】:

this React answer 中的观点在 Angular、任何其他框架或原生 javascript/TypeScript 中仍然有效。

类原型方法是 ES6,类箭头方法不是。箭头方法属于class fields proposal,而不是现有规范的一部分。它们是用 TypeScript 实现的,也可以用 Babel 进行转译。

通常使用原型method() ... 比使用箭头method = () =&gt; ... 更可取,因为它更灵活。

回调

箭头方法提供的唯一真正机会是它可以无缝地用作回调:

class Class 
  method = () =>  ... 


registerCallback(new Class().method);

如果原型方法应该用作回调,它应该被额外绑定,这应该最好在构造函数中完成:

class Class 
  constructor() 
    this.method = this.method.bind(this);
  

  method()  ... 


registerCallback(new Class().method);

可以在 TypeScript 和 ES Next 中使用像 bind-decorator 这样的装饰器,为构造函数中的方法绑定提供更简洁的替代方案:

import bind from 'bind-decorator';

class Class 
  @bind
  method()  ... 

继承

箭头方法也限制子类使用箭头方法,否则它们不会被覆盖。如果忽略了箭头,就会产生问题:

class Parent 
  method = () =>  ... 


class Child extends Parent 
  method()  ...  // won't override Parent method

不能在子类中使用super.method(),因为super.method 引用了不存在的Parent.prototype.method

class Parent 
  method = () =>  ... 


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

混合

原型方法可以有效地用于 mixins。 Mixin 对于多重继承或修复 TypeScript 方法可见性问题很有用。

由于箭头方法在类原型上不可用,因此无法从类外部访问:

class Parent 
  method = () =>  ... 


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

测试

原型方法提供的一个有价值的特性是它们可以在类实例化之前访问,因此它们可以在测试中被监视或模拟,即使它们在构造之后立即被调用:

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

当方法是箭头时,这是不可能的。

TL;DR:原型和箭头类方法之间的选择似乎是一个品味问题,但实际上原型方法的使用更具远见。您可能通常希望避免使用箭头类方法,除非您确定它们不会造成不便。如果您将原型方法作为回调传递,请不要忘记在原型方法上使用 bind

【讨论】:

非常好的答案,但是在您的 TL;DR 中,如果您在类本身内部使用粗箭头调用基于原型的方法,则不一定需要使用 .bind? @RichardWatts Prototype method bind 和箭头是互斥的。你的意思是像arrowMethod = () =&gt; this.anotherPrototypeMethod()这样的东西吗?不,这里不需要bind 抱歉,我不够清楚。我在上课,我定义了基于普通类的方法public mySuccessMethod(success) ... public myErrorMethod(error) ... 发生了异步调用,并且在我的订阅(rxjs)中我有asyncCall.subscribe(success =&gt; mySuccessMethod(success), error =&gt; myErrorMethod(error)) 在这个.subscribe 我完全不需要使用.bind 由于事实上我使用的是胖箭头,它给出了正确的上下文? @RichardWatts 没错,你在这里并不需要bind。但是使用像.subscribe(this.myMethod) 这样的绑定方法是有益的,因为1)你不需要枚举args,特别是如果有多个arg(并且(...args) =&gt; myMethod(...args)不适用于TS类型)2)如果这篇文章代码经常被调用,你不需要每次调用时都创建箭头函数 3)这对单元测试有点好处,你可以断言expect(obs.subscribe).toHaveBeenCalledWith(obj.myMethod) - 这是你不能用匿名函数做的事情。除此之外,箭头还可以。 感谢您的回复,所以在我展示的示例中,我不需要枚举 args,因为传递给这些方法的参数只是来自 rx 的一个对象。如果您每次都对使用箭头函数感到困扰,我有点理解使用绑定的论点,但我想这取决于个人喜好,因为我更喜欢 .bind()。重新测试——这不取决于吗?因为如果你做正确的嘲笑和存根无关紧要?很抱歉没有尝试听起来很聪明!

以上是关于我应该在 Angular 的类中将方法写成箭头函数吗的主要内容,如果未能解决你的问题,请参考以下文章

使用箭头键的 Angular2 导航

如何将 Angular Material 扩展面板箭头图标定位在左侧

JavaScript箭头函数

js 箭头函数

ES6 箭头函数是不是与 Angular 不兼容?

class上箭头函数和普通函数的区别