web-dev-qa-db-ja.com

Angularのクラスで矢印関数としてメソッドを書くべきですか

Angularでは、ES2015の矢印関数としてクラスメソッドを書くことは技術的に可能ですが、実際に誰かがそれを行うのを見たことはありません。この単純なコンポーネントを例にとります:

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

これは問題なく動作します。違いはありますか?そして、なぜこれを使用するべきか、または使用すべきでないのですか?

15
Gideon

this React answer で行われたポイントは、Angular、その他のフレームワーク、またはVanilla JavaScript/TypeScriptでも引き続き有効です。

クラスプロトタイプメソッドはES6ですが、クラスアローメソッドはそうではありません。 Arrowメソッドは class fields proposal に属し、既存の仕様の一部ではありません。それらはTypeScriptで実装され、Babelでトランスパイルすることもできます。

柔軟性が高いため、一般的に矢印method() { ... }よりもプロトタイプmethod = () => { ... }を使用することをお勧めします。

コールバック

Arrowメソッドが提供する唯一の真の機会は、それがコールバックとしてシームレスに使用できることです。

class Class {
  method = () => { ... }
}

registerCallback(new Class().method);

プロトタイプメソッドをコールバックとして使用する必要がある場合は、さらにバインドする必要があります。これは、コンストラクターで行うのが望ましいです。

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

  method() { ... }
}

registerCallback(new Class().method);

bind-decorator のようなデコレータをTypeScriptおよびES Nextで使用して、コンストラクタでのメソッドバインディングのより簡潔な代替手段を提供できます。

import bind from 'bind-decorator';

class Class {
  @bind
  method() { ... }
}

継承

Arrowメソッドは、子クラスがarrowメソッドを使用することも制限します。そうでない場合、子クラスはオーバーライドされません。これにより、矢印を見落とした場合に問題が発生します。

class Parent {
  method = () => { ... }
}

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

super.methodは存在しないParent.prototype.methodを参照するため、子クラスでsuper.method()を使用することはできません。

class Parent {
  method = () => { ... }
}

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

ミックスイン

プロトタイプメソッドは、ミックスインで効率的に使用できます。ミックスインは、多重継承やTypeScriptメソッドの可視性の問題を修正するのに役立ちます。

Arrowメソッドはクラスプロトタイプでは利用できないため、クラスの外部から到達することはできません。

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:プロトタイプメソッドとarrowクラスメソッドのどちらを選択するかは好みの問題のようですが、実際にはプロトタイプメソッドの使用はより先見性があります。通常、矢印クラスのメソッドは、それらが不都合を引き起こさないことが確実でない限り、避けたいでしょう。コールバックとして渡す場合は、プロトタイプメソッドでbindを使用することを忘れないでください。

28
Estus Flask

クラス矢印関数の適切な使用例は、関数を別のコンポーネントに渡し、現在のコンポーネントのコンテキストを関数に保存する場合です。

@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
Milad

文書化されているように、AOTコンパイルを実行する必要がある場合、矢印関数の使用を控えなければならないケースは1つだけあります 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 {}
1