web-dev-qa-db-ja.com

NodeJS(ES6):バインドを使用するデザインパターン

この質問は、Javascriptクラスでのthis演算子の解決に関連しています。 NodeJSがES6クラスをサポートするようになりました。この間に直面する問題は、クラスの新しいインスタンスが作成されて変数に割り当てられるとすべてが正常に機能することですが、特定のクラスメソッドを変数に割り当てると、この変数のスコープが失われます。

class A {

    publicMethod() {
        console.log('Public Method');
        console.log('Calling Private Method');
        this._privateMethod();   
    }

    _privateMethod() {
        console.log('Private Method');
    }

}

class B extends A {
    constructor() {
        super();
        return  {
            publicMethod: this.publicMethod
        }
    }
}

class C extends A {
    constructor() {
        super();
        return  {
            publicMethod: this.publicMethod.bind(this)
        }
    }
}

問題は2つの部分です。

  1. クラスメソッドでバインドを使用すると、パフォーマンスにどのような影響がありますか?アプリケーション全体で練習として使用されていると考えてください。 (BからpublicMethodにアクセスすると、エラーがスローされますが、AまたはCの場合はスローされません)
  2. 可視性をサポートするために、クラスコンストラクターから何も返さずに、パブリック向けのメソッドをいくつか返すことは適切な方法であり、クラスの可視性を有効にします。 (BおよびCと同様)
3

コンストラクターでの明示的な戻り

コンストラクタで明示的に返すと、元のプロトタイプがなくなったオブジェクトが返されます。元のプロトタイプが使用されるように処理した場合、これで問題ありません。

しかし、それは珍しいことであり、これがどのようにあなたを助けることができるかについては本当に考えられません。その時点では、通常の関数を使用するだけで済むためです。ES6クラスの構文は、もはや役に立ちません。

矢印関数

私はあなたがやった方がいいと思います:

class B2 extends A {
    constructor() {
        super();
        this.publicMethod = () => super.publicMethod()
    }
}

これはbindを使用することとほぼ同じですが、矢印関数を使用する方がわかりやすいと思います。

クラスのプロパティ

あるいは、babelは クラスプロパティ をサポートしています。これにより、次のようなアロー関数(B2など)を使用してフィールドを設定するだけに変換されるコードを記述できます。

class A {
    constructor() {
        this.i = 0
    }

    publicMethod = () => this.i++
}

バインド/矢印関数を使用しない

Bind/arrow関数を使用する必要がなく、理想的な状況になるようにアプリケーションを設計することもできます。

class B3 extends A {
    constructor() {
        super();
    }

    publicMethod() {
        super.publicMethod();
    }
}

メソッドではなく呼び出しをラップする

Bind/arrow関数を使用していない場合でも、コンテキストが保持されていることを確認する必要がある場合があります。

私の意見では、最終的にコンテキストを決定するのは呼び出し元であり、作成者が設定したかどうかを理解するのではなく呼び出し元の義務である必要があるため、矢印関数またはバインド内でメソッド呼び出しのコンテキストをラップするだけの方が一貫していますあなたのために正しくアップします。あなたはそうすることができます:

const a = new A();
const callback = () => a.method();
el.addEventListener('event', callback);

バインド/矢印関数を使用せずに同様の効果を達成するためのコンテキストを取り入れた高次関数を使用して、同じ効果を達成することもできます。

function wrapCall(a) {
    return function(event) { a.method(event) };
}

const a = new A();
const callback = wrapCall(a);
el.addEventListener('event', callback);

パフォーマンス

関数でbindを使用しても、特にコストはかかりません。ただし、オブジェクトの各インスタンスが元の関数に基づいて関数を含むプロパティを取得するため、関数参照が多くなります。通常のメソッドでは、関数がオブジェクトのプロトタイプに保存されているため、すべてのインスタンスで関数を共有できます。

Reactレンダリング中など)何かが発生するたびに新しい関数を作成する必要がある場合、不必要に多くの関数を作成できます。通常、バインド/矢印関数を使用して保存するだけの方が安価ですフィールドまたはアクセス可能な場所として。ただし、DOMにバインドする関数のインスタンスを1つだけ作成する必要がある場合は、上記の例のようにイベントを追加するときに、矢印関数またはバインドを使用する方が安価です。

矢印のオーバーヘッドと有界関数

技術的には、矢印関数は有界関数よりも遅く、通常の関数は両方よりも高速です。ただし、これは実際には重要ではないものの1つです。関数呼び出しのオーバーヘッドがアプリケーションのパフォーマンスを低下させることはおそらくありません。余分な参照を作成して不要なメモリを使用することは大きな問題であり(ほんのわずかですが)、時間をかけるだけの価値があります。

結論

残念ながら、JSの世界では、ベストプラクティスはかなり明確に定義されておらず、同じことを行う方法はたくさんあります。どれも明らかに他のものより優れていません。おそらく、状況に応じて自分で決断しなければならないでしょう。

しかし、あなたが見ることができるより著名な個人が私が遭遇したトピックについて議論するいくつかのリソースがあります。彼らは反応に焦点を当てていますが、私はあなたが行の間に読んだ場合にも原則が適用されると私は信じています。

1
Kevin Raoofi