回答:
基本的な違いは、コンストラクター関数がnew
キーワードと共に使用されることです(これにより、JavaScriptは自動的に新しいオブジェクトを作成this
し、関数内でそのオブジェクトに設定して、オブジェクトを返します)。
var objFromConstructor = new ConstructorFunction();
ファクトリ関数は「通常の」関数のように呼び出されます。
var objFromFactory = factoryFunction();
ただし、「ファクトリ」と見なされるには、オブジェクトの新しいインスタンスを返す必要があります。ブール値などを返しただけの場合は、「ファクトリ」関数と呼ばないでください。これは、のように自動的に行われるわけではありませんがnew
、場合によっては柔軟性が向上します。
非常に単純な例では、上記で参照した関数は次のようになります。
function ConstructorFunction() {
this.someProp1 = "1";
this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };
function factoryFunction() {
var obj = {
someProp1 : "1",
someProp2 : "2",
someMethod: function() { /* whatever */ }
};
// other code to manipulate obj in some way here
return obj;
}
もちろん、ファクトリー関数をその単純な例よりもはるかに複雑にすることができます。
ファクトリー関数の利点の1つは、返されるオブジェクトが、パラメーターに応じていくつかの異なるタイプになる可能性がある場合です。
someMethod
によって返されたオブジェクトは含まれていません。そのため、少しぼやけてしまいます。ファクトリ関数の内部では、1つだけを実行するとvar obj = { ... , someMethod: function() {}, ... }
、返される各オブジェクトが異なるコピーを保持することになり、そのコピーはsomeMethod
望ましくない場合があります。これは、ファクトリ関数の使用new
とprototype
内部で役立ちます。
new
に、コンストラクター関数での使用を忘れるバグを残したくないという理由だけでファクトリー関数を使用しようとする人もいます。コンストラクターをファクトリー関数の例に置き換える方法を確認する必要があるのはそのときだと思いました。そこで、例の一貫性が必要だと思いました。とにかく、答えは十分に有益です。これは私が提起したかった点であり、私が答えの質を何らかの方法で引き下げているわけではありません。
new
内部的に使用Object.create()
することも、特定のプロトタイプでオブジェクトを作成するために使用することもできます。
ほとんどの本はコンストラクタを使用することを教え、 new
this
新しいオブジェクトを参照します
一部の人々は方法がvar myFoo = new Foo();
読むのが好きです。
インスタンス化の詳細が(new
要件を介して)呼び出し元のAPIにリークされるため、すべての呼び出し元はコンストラクターの実装に密接に結合されます。ファクトリーの追加の柔軟性が必要な場合は、すべての呼び出し元をリファクタリングする必要があります(確かに、ルールではなく例外的なケースです)。
忘却new
はよくあるバグですif (!(this instanceof Foo)) { return new Foo() }
。コンストラクタが正しく呼び出されるように、ボイラープレートチェックを追加することを強く検討してください()。編集:ES6(ES2015)以降new
、class
コンストラクターを忘れることができません。
instanceof
チェックを行うと、new
必要かどうかが曖昧になります。私の意見では、そうであってはなりません。new
要件を効果的に短絡しました。つまり、欠点#1をなくすことができます。しかし、名前を除いて、ファクトリー関数が追加されただけであり、定型文、大文字、柔軟性の低いthis
コンテキストが追加されています。
しかし、私の主な懸念は、それがオープン/クローズドの原則に違反していることです。コンストラクターのエクスポートを開始し、ユーザーがコンストラクターの使用を開始してから、代わりにファクトリの柔軟性が必要であることに気づきます(たとえば、実装を切り替えてオブジェクトプールを使用するか、実行コンテキスト間でインスタンス化するか、またはプロトタイプOOを使用すると、継承の柔軟性が向上します)。
しかし、行き詰まっています。でコンストラクターを呼び出すすべてのコードを壊さずに変更を行うことはできませんnew
。たとえば、パフォーマンス向上のためにオブジェクトプールの使用に切り替えることはできません。
また、コンストラクターを使用すると、instanceof
実行コンテキスト全体で機能せず、コンストラクターのプロトタイプがスワップアウトされた場合に機能しなくなります。またthis
、コンストラクターから戻り始め、任意のオブジェクトのエクスポートに切り替えた場合も失敗します。これは、コンストラクターでファクトリーのような動作を有効にするために実行する必要があります。
少ないコード-ボイラープレートは必要ありません。
任意のオブジェクトを返し、任意のプロトタイプを使用できるため、同じAPIを実装するさまざまなタイプのオブジェクトを作成する柔軟性が高まります。たとえば、HTML5プレーヤーとFlashプレーヤーの両方のインスタンスを作成できるメディアプレーヤー、またはDOMイベントやWebソケットイベントを発行できるイベントライブラリ。ファクトリは、実行コンテキスト全体でオブジェクトをインスタンス化し、オブジェクトプールを利用して、より柔軟なプロトタイプ継承モデルを可能にすることもできます。
ファクトリからコンストラクタに変換する必要がないため、リファクタリングが問題になることはありません。
の使用について曖昧さはありませんnew
。しないでください。(それはthis
悪い振る舞いをします、次のポイントを見てください)。
this
は通常どおりに動作します。したがって、これを使用して、他のメソッド呼び出しと同様に、親オブジェクトにアクセスできます(たとえば、内player.create()
、this
参照player
、call
および期待どおりにをapply
再割り当てしthis
ます。親オブジェクトにプロトタイプを保存すると、機能を動的にスワップアウトし、オブジェクトのインスタンス化に非常に柔軟なポリモーフィズムを有効にする優れた方法です。
大文字にするかどうかについてのあいまいさはありません。しないでください。Lintツールは文句を言うでしょう、そしてあなたはを使おうとするように誘惑されnew
、そしてあなたは上記の利点を取り消すでしょう。
一部の人々は方法var myFoo = foo();
またはvar myFoo = foo.create();
読書が好きです。
new
期待どおりに動作しません(上記を参照)。解決策:使用しないでください。
this
新しいオブジェクトを参照しません(代わりに、コンストラクターがドット表記または角括弧表記で呼び出された場合(例:foo.bar())- this
をfoo
参照-他のすべてのJavaScriptメソッドと同じように-利点を参照してください)。
new
がオープン/クローズの原則に違反するということです。これらのコメントで許可されているよりもはるかに大きな議論については、medium.com / javascript-scene /…を参照してください。
new
キーワードなしで返すので、new
キーワードが実際に追加の読みやすさを提供するとは思いません。IMO、発信者がさらに入力できるようにするために、フープをジャンプするのはばかげているようです。
コンストラクターは、呼び出したクラスのインスタンスを返します。ファクトリ関数は何でも返すことができます。任意の値を返す必要がある場合、またはクラスに大きなセットアッププロセスがある場合は、ファクトリ関数を使用します。
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
new
プロトタイプ化されたオブジェクトを作成し、作成したオブジェクトを値としてUser.prototype
呼び出します。User
this
new
オペランドの引数式をオプションとして扱います。
let user = new User;
引き起こすnew
呼び出すためにUser
、引数なしで。
new
コンストラクタがオブジェクトの値を返し、それが代わりに返されない限り、作成したオブジェクトを返します。これは、ほとんどの場合無視できるエッジケースです。
コンストラクター関数によって作成されたオブジェクトは、コンストラクターのprototype
プロパティからプロパティを継承instanceOf
し、コンストラクター関数の演算子を使用してtrueを返します。
上記の動作は、コンストラクタprototype
を既に使用した後でコンストラクタのプロパティの値を動的に変更すると失敗する可能性があります。そうすることはまれであり、コンストラクターがclass
キーワードを使用して作成された場合は変更できません。
extends
キーワードを使用して、コンストラクター関数を拡張できます。
コンストラクター関数はnull
エラー値として返すことはできません。これはオブジェクトデータタイプではないため、によって無視されnew
ます。
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
ここでは、ファクトリ関数はなしで呼び出されnew
ます。関数は、その引数とそれが返すオブジェクトのタイプである場合、直接または間接的な使用に対して完全に責任があります。この例では、引数から設定されたいくつかのプロパティを持つ単純な[オブジェクトオブジェクト]を返します。
オブジェクト作成の実装の複雑さを呼び出し元から簡単に隠します。これは、ブラウザのネイティブコード関数に特に役立ちます。
ファクトリ関数は常に同じタイプのオブジェクトを返す必要はなくnull
、エラーインジケーターとして返すこともできます。
単純なケースでは、ファクトリ関数は構造と意味が単純な場合があります。
返されるオブジェクトは通常、ファクトリ関数のprototype
プロパティを継承せず、から返さfalse
れinstanceOf factoryFunction
ます。
extends
拡張オブジェクトは、ファクトリ関数で使用されるコンストラクターprototype
のprototype
プロパティからではなく、ファクトリ関数プロパティを継承するため、キーワードを使用してファクトリ関数を安全に拡張することはできません。
工場は「常に」優れています。オブジェクト指向言語を使用する場合
実装(newで作成された実際のオブジェクト)は、ファクトリーユーザー/コンシューマーには公開されません。つまり、工場の開発者は、契約に違反しない限り、新しい実装を拡張して作成できます。また、工場の消費者は、コードを変更せずに新しいAPIを利用できます。彼らが新しいものを使用し、「新しい」実装が登場した場合、「新しい」を使用するすべての行を変更して、「新しい」実装を使用する必要があります。工場では、コードは変更されません...
工場-何よりも優れている-春のフレームワークは、このアイデアを中心に完全に構築されています。
工場は抽象化の層であり、すべての抽象化と同様に、複雑さのコストがあります。ファクトリベースのAPIに遭遇した場合、指定されたAPIのファクトリが何かを理解することは、APIコンシューマーにとって困難な場合があります。コンストラクターの場合、発見は簡単です。
アクターとファクトリーの間で決定するとき、複雑さは利益によって正当化されるかどうか決定する必要があります。
これ以外の、または未定義の何かを返すことにより、Javascriptコンストラクタが任意のファクトリになる可能性があることに注意する価値があります。したがって、jsでは、両方の世界で最高の機能(発見可能なAPIとオブジェクトのプール/キャッシング)を利用できます。
new
はthis
、を必要とする、の動作を変更する、戻り値を変更する、プロトタイプ参照を接続する、有効にするinstanceof
(これは嘘であり、この目的には使用しないでください)によって複雑さを追加します。表面的には、これらはすべて「機能」です。実際には、コードの品質に悪影響を及ぼします。