Welcome to Our Website

JavaScriptファクトリ関数vsコンストラクタ関数vsクラス

ES6より前は、JavaScriptのファクトリ関数とコンストラクタ関数の違いについて多くの混乱がありました。 ES6には`class`キーワードがあるため、多くの人がコンストラクタ関数の多くの問題を解決したと考えているようです。 あなたがまだ気づく必要がある大きな違いを探ってみましょう。,

まず、それぞれの例を見てみましょう。

これらの戦略はそれぞれ、共有プロトタイプにメソッドを格納し、オプションでコンストラクタ関数クロージャを介してプライベートデータをサポートします。 言い換えれば、それらはほとんど同じ機能を持ち、ほとんど同じ意味で使用することができます。

JavaScriptでは、どの関数でも新しいオブジェクトを返すことができます。 コンストラクタ関数またはクラスでない場合は、ファクトリ関数と呼ばれます。,

ES6クラスはコンストラクタ関数にdesugarなので、コンストラクタ関数について次のすべてはES6クラスにも適用されます。

class Foo {}console.log(typeof Foo); // function

コンストラクタは呼び出し元にnewキーワードを使用するよう強制します。 工場はそうではありませんそれはそれですが、それにはいくつかの関連する副作用がありまそれでは、newキーワードは何をしますか?,

注:instanceを使用して新しく作成されたインスタンスを参照し、Constructorインスタンスを作成したコンストラクタ関数またはクラスを参照します。

  1. 新しいインスタンスオブジェクトをインスタンス化し、thisコンストラクタ内でそれにバインドします。
  2. instance.__proto__Constructor.prototypeにバインドします。li>
  3. 2の副作用として、instance.__proto__.constructorConstructorにバインドします。,
  4. は暗黙的にthisを返します。instanceを参照します。

コンストラクタの利点&`class`

  • ほとんどの本では、クラスまたはコンストラクタを使用するように教えています。
  • this新しいオブジェクトを参照します。li>
  • myFoo = new Foo()の読み方が好きな人もいます。
  • マイクロ最適化パフォーマンスの利点があるかもしれませんが、コードをプロファイリングして問題であることを証明しない限り、それについて心配すべきではありません。,

コンストラクタの欠点&`class`

ES6より前は、newを忘れることは非常に一般的なバグでした。 これに対抗するために、多くの人が定型文を使用して強制しました。

function Foo() {
if (!(this instanceof Foo)) { return new Foo(); }
}

ES6+(ES2015)でnewなしでクラスコンストラクタを呼び出そうとすると、常にエラーがスローされます。 ファクトリ関数でクラスをラップせずに呼び出し元にnew要件を強制することを避けることはできません。,

インスタンス化の詳細は、呼び出し元のAPIに(”新しい”要件を介して)漏洩します。

すべての呼び出し元は、コンストラクタの実装に密接に結合されています。 工場のさらなる柔軟性が必要な場合、リファクタリングは画期的な変更です。 クラスからファクトリへのリファクタリングは、Martin Fowler、Kent Beck、John Brant、William Opdyke、およびDon Robertsによる独創的なリファクタリングの本”Refactoring:Improving the Design of Existing Code”に登場するほど一般的です。,

コンストラクタはオープン/クローズの原則を破ります

new要件のため、コンストラクタ関数はオープン/クローズの原則に違反します。APIは拡張のために開いている必要がありますが、変更のために閉じている必要があります。

私は、クラスからファクトリへのリファクタリングは、すべてのコンストラクタの標準拡張とみなされるべきであると主張しています。,

コンストラクターまたはクラスのエクスポートを開始し、ユーザーがコンストラクターを使用し始めると、ファクトリーの柔軟性が必要になります(たとえば、実装をオブジェクトプールを使用するように切り替えたり、実行コンテキスト間でインスタンス化したり、代替プロトタイプを使用して継承の柔軟性を高めたりするなど)。,

残念ながら、JavaScriptでは、コンストラクタまたはクラスからファクトリに切り替えることは重大な変更です。

上記の例では、クラスから始めますが、さまざまな種類のカーバンドルを提供する機能を追加したいと考えています。 そうするために、工場は異なった車の束のために代わりとなるプロトタ この手法を使用して、メディアプレーヤーインターフェイスのさまざまな実装を格納し、プレーヤーが制御する必要があるメディアのタイプに基づいて正しい,

コンストラクタを使用すると、欺瞞的な`instanceof`が有効になります

コンストラクタのファクトリリファクタリングへの重大な変更の一つは、instanceof 時には、コード内の型チェックガードとしてinstanceofを使用するように誘惑されることがあります。 それは非常に問題になります。 私はあなたが避けることをお勧めしますinstanceof

`p>’instanceof`があります。,

instanceof強く型付けされた言語で同様のチェックが行われると予想される方法で型チェックを行いません。 代わりに、オブジェクトの__proto__オブジェクトをConstructor.prototypeプロパティと比較するidチェックを行います。

たとえば、iframeのような異なるメモリ領域では動作しません(3rd party JavaScript埋め込みのバグの一般的な原因)。 また、Constructor.prototypeが置き換えられた場合も機能しません。,

クラスまたはコンストラクタ(thisを返し、Constructor.prototypeにリンクされている)から始めて、任意のオブジェクト(Constructor.prototypeにリンクされていない)のエクスポートに切り替えると失敗します。

要するに、instanceofは、コンストラクタからファクトリへの切り替えが重大な変更である別の方法です。

クラスを使用する利点

  • 便利な自己完結型構文。,
  • JavaScriptでクラスをエミュレートするための単一の標準的な方法です。 ES6以前は、人気のあるライブラリにいくつかの競合する実装がありました。
  • クラスベースの言語の背景からの人々により精通しています。

クラスを使用する欠点

コンストラクタのすべての欠点に加えて、

  • ユーザーがextendsキーワードを使用して問題のあるクラス階層,

クラス階層は、脆弱な基本クラスの問題、gorilla bananaの問題、必要による重複の問題など、オブジェクト指向設計におけるよく知られた問題の束につなが 残念なが 詳細については、”JavaScriptの二つの柱:プロトタイプOO”と”開発チームの死のスパイラルの中”を読んでください。,

コンストラクターとファクトリーの両方を使用して問題のある継承階層を作成することもできますが、`extends`キーワードを使用すると、classは間違った道を 言い換えれば、より柔軟な構成のhas-aまたはcan-do関係ではなく、柔軟性のない(そしてしばしば間違っている)is-a関係の面で考えることを奨励します。

アフォーダンスは、特定のアクションを実行する機会を与える機能です。, 例えば、ノブはねじれを与え、レバーは引っ張りを与え、ボタンは押すことを与えるなど…

ファクトリを使用する利点

ファクトリはコンストラクタ関数やクラスよりもはるかに柔軟であり、`extends`キーワードと深い継承階層で誘惑することによって人々を間違った道に導くことはありません。 多くのより安全なコード再利用機構のすべて賛以上クラスの継承を含め、機能しています。,

任意のオブジェクトを返し、任意のプロトタイプを使用する

たとえば、同じAPIを実装するさまざまなタイプのオブジェクトを簡単に作成できます。

ファクトリは、実行コンテキスト間でオブジェクトをインスタンス化し、オブジェクトプールを利用し、より柔軟なプロトタイプ継承モデルを可能に,

リファクタリングの心配はありません

ファクトリからコンストラクタに変換する必要はありませんので、リファクタリングは決して

`new`はありません

newの使用についてのあいまいさはありません。 (それは作るthisひどく動作します、次のポイントを参照してください)。

標準の’this`動作

this通常どおり動作するので、親オブジェクトにアクセスするために使用できます。 たとえば、内部player.create()this他のメソッド呼び出しと同じように、playerを参照します。, call()およびapply()また、this期待どおりに再割り当てします。

`myFoo=createFoo()`の読み方が好きな人もいます

  • インスタンスからFactory.prototypeへのリンクを作成しません—しかし、欺瞞的なinstanceofを取得しないため、これは実際には良いことです。 代わりに、instanceofは常に失敗します。 特典をご覧ください。
  • thisファクトリ内の新しいオブジェクトを参照しません。 特典をご覧ください。,
  • マイクロ最適化ベンチマークでは、コンストラクタ関数よりも実行が遅くなることがあります。 遅いパスはまだ非常に高速です—古いコンピュータ上のops/秒の何百万人。 こる可能性がありま懸念図書館またはフレームワークのコードでアプリケーションコード. マイクロ最適化を使用する前に、常にユーザーの視点からベンチマーク。

結論

私の意見では、class便利な構文を持っているかもしれませんが、それは不注意なユーザーがクラス継承の岩の上でクラッシュするという事実を補うことはできません。, また、将来的にはファクトリにアップグレードしたいかもしれませんが、newキーワードと、クラスからファクトリへの移行が重大な変更であるという事実のため、すべての呼び出し元がコンストラクタ関数に密に結合されるため、リスクもあります。

コールサイトをリファクタリングするだけでよいと考えているかもしれませんが、大規模なチームでは、または作業しているクラスが公開APIの一部である場合、コントロールにないコードを壊す可能性があります。 言い換えれば、呼び出し元をリファクタリングすることがオプションであるとは限りません。,

ファクトリーの素晴らしいところは、より強力で柔軟性が高いだけでなく、チーム全体やAPIユーザーベース全体に、シンプルで柔軟で安全なパターンを使用するよう促す最も簡単な方法でもあるということです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です