Wait, what? 大きな疑問に答えるもうひとつの記事です。 サービスかファクトリか、どちらを使うべきか? そのトピックについて議論するリソースがインターネット上に大量にあるため、これはもう必要ないように思われます。 この質問は、今でも毎週、あるいはそれくらい、さまざまなチャンネルで出てきますし、StackOverflowのトップ10の答えを読んでも、まだあまり明確ではありません。 にもかかわらず、ウェブ上の現在のリソースは、特に最近のウェブプラットフォームの動きを考慮すると、実際のベストプラクティスを促進していないようにも見えるのです。 ES6 I’m looking at you!
この記事では、サービスとファクトリーの違いと、なぜファクトリーよりもサービスを優先したいのかをきっぱりと説明します。
The difference between services and factories
Okay, so what is the difference between a service and a factory in AngularJS? ご存知のように、次のようにサービスを定義できます。
app.service('MyService', function () { this.sayHello = function () { console.log('hello'); };});
.service()
は、名前とサービスを定義する関数を受け取る、モジュール上のメソッドです。 非常に簡単です。 いったん定義すると、次のように、コントローラ、ディレクティブ、フィルターなどの他のコンポーネントで、その特定のサービスを注入して使用できます:
app.controller('AppController', function (MyService) { MyService.sayHello(); // logs 'hello'});
わかりました。 今度はファクトリーと同じものです:
app.factory('MyService', function () { return { sayHello: function () { console.log('hello'); } }});
繰り返しますが、.factory()
はモジュール上のメソッドで、名前と関数も受け取り、ファクトリーを定義します。 サービスに対して行ったのとまったく同じ方法で、これを注入して使用することができます。
さて、ファクトリーで this
を操作する代わりに、オブジェクト リテラルを返していることがわかるかもしれません。 これはなぜでしょうか。 それは、サービスがコンストラクター関数であるのに対し、ファクトリーはそうでないからです。 Angularの世界の奥深くに、サービスのコンストラクタ関数でObject.create()
を呼び出すコードがあり、それがインスタンス化されたときに呼び出されるのです。
このことをもう少し明確にするために、Angular のソース コードを見てみましょう。 以下は、factory()
関数がどのようなものかです。
function factory(name, factoryFn, enforce) { return provider(name, { $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn });}
これは、渡された名前とファクトリ関数を受け取り、基本的に同じ名前のプロバイダーを返し、それは $get
関数を持つ、私たちのファクトリ関数のメソッドです。 では、このプロバイダーとは何なのでしょうか。 インジェクタに特定の依存関係を要求すると、基本的に $get()
メソッドを呼び出して、対応するプロバイダにそのサービスのインスタンスを要求します。
言い換えると、MyService
をどこかにインジェクトすると、舞台裏で何が起こるかというと、
MyServiceProvider.$get(); // return the instance of the service
さて、ファクトリ関数が呼ばれるだけですが、サービス コードについてはどうでしょうか。
function service(name, constructor) { return factory(name, );}
Oh look, it turns out that we call service()
it actually calls factory()
.This is an another snippet:
function service(name, constructor) { return factory(name, );}
Oh look, it turns out that we call service()
when it actually calls factory()
. しかし、サービスのコンストラクタ関数をそのままファクトリに渡すだけではありません。 与えられたコンストラクタでオブジェクトをインスタンス化するようにインジェクタに依頼する関数を渡しているのです。 つまり、サービスが定義済みファクトリを呼び出すと、対応するプロバイダの$get()
メソッドに行き着くということです。 $injector.instantiate()
は、最終的にコンストラクタ関数でObject.create()
を呼び出すメソッドです。
さて、service()
または factory()
のどちらを使用しても、サービスのプロバイダーを作成するファクトリーが常に呼び出されることがわかりました。 そこで、Angularの歴史の中で最も多く聞かれた質問を紹介します。
どちらを使うべきですか?
インターネット上でこの質問をすると、いくつかの記事とStackOverflowの答えにたどり着きます。 1 つ目は、この回答です。 この回答では、
「基本的に、サービスとファクトリーの違いは次のとおりです」
app.service('myService', function() { // service is just a constructor function // that will be called with 'new' this.sayHello = function(name) { return "Hi " + name + "!"; };});app.factory('myFactory', function() { // factory returns an object // you can run some code before return { sayHello : function(name) { return "Hi " + name + "!"; } }});
私たちは、裏で何が起こっているかをすでに知っていますが、この回答では別のコメントが追加されています。 それは、オブジェクト リテラルを返す前にコードを実行することができると言うことです。 これは、基本的に、いくつかの設定事項や、条件付きでオブジェクトを作成するかどうかを行うことができます。 サービスはコンストラクタ関数ですが、しかし、だからといって、追加の作業やオブジェクト リテラルを返すことを妨げるわけではありません。 実際、JavaScript のコンストラクタ関数は好きなものを返すことができます。 したがって、サービスのコードを取り、基本的にファクトリーとまったく同じことを行うように書くことができます:
app.service('MyService', function () { // we could do additional work here too return { sayHello: function () { console.log('hello'); }; }});
Hoppla, so what now? 私たちは、サービスの書き方次第では、もうこの 2 つの間にまったく違いがないことに気づきました。 大きな疑問が残ります。
サービスでは ES6 クラスを使用できます
もちろん、そのようにサービスを書くことは、コンストラクタ関数として呼び出されるため、そのように使用する必要があり、一種の逆生産的です。 では、他に利点はあるのでしょうか? はい、あります。 ES6に移行する際には、可能な限りサービスを使用した方が良いことが分かっています。 その理由は、サービスはコンストラクタ関数であり、ファクトリはそうではないからです。 ES5 でコンストラクタ関数を使用すると、ES6 に移行するときに ES6 クラスを簡単に使用できます。
たとえば、次のようにコードを ES6 で書き直すことができます。 それについては、今日の Angular で ES6 を使用するで書きましたので、まだその記事を読んでいない場合は、それを確認することをお勧めします。
Factory では、これは不可能で、単に関数として呼び出されます。 この記事ですべてが明らかになり、何を使用したらよいかわからない場合は、サービスよりもファクトリーを使用しないようにすることをお勧めします。
Angular Master Class では、この他にも多くのことを学ぶことができます。