angular.factory() と angular.service() の両方がサービスの宣言に使用されています。しかし、I は公式文書のどこにもangular.service
を見つけることができません。
2つの方法の違いは何ですか?どちらを使用する必要がありますか(それらが異なることをすると仮定して)。
angular.service('myService', myServiceFunction);
angular.factory('myFactory', myFactoryFunction);
私はこのように考えてみるまで、この概念に頭を包むのに苦労しました。
Service :あなたが書いた function は new - edになるでしょう:
myInjectedService <---- new myServiceFunction()
Factory :あなたが書いた function (コンストラクタ)は invoke :になります。
myInjectedFactory <--- myFactoryFunction()
それをどうするかはあなた次第ですが、いくつかの便利なパターンがあります。
function myServiceFunction() {
this.awesomeApi = function(optional) {
// calculate some stuff
return awesomeListOfValues;
}
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();
function myFactoryFunction() {
var aPrivateVariable = "yay";
function hello() {
return "hello mars " + aPrivateVariable;
}
// expose a public API
return {
hello: hello
};
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.hello = myInjectedFactory.hello();
function myFactoryFunction() {
return function() {
var a = 2;
this.a2 = function() {
return a*2;
};
};
}
---------------------------------------------------------------------------------
// Injected in your controller
var myShinyNewObject = new myInjectedFactory();
$scope.four = myShinyNewObject.a2();
あなたは両方で同じことを成し遂げることができます。ただし、場合によっては factory を使用すると、より単純な構文で注入可能オブジェクトを作成するための柔軟性が少し向上します。これは、myInjectedServiceは常にオブジェクトである必要がありますが、myInjectedFactoryはオブジェクト、関数参照、または任意の値になる可能性があるからです。たとえば、(上記の最後の例のように)コンストラクタを作成するサービスを作成した場合は、次のようにインスタンス化する必要があります。
var myShinyNewObject = new myInjectedService.myFunction()
これは間違いなくこれよりも望ましくありません:
var myShinyNewObject = new myInjectedFactory();
(ただし、このタイプのパターンを最初から使用することには慎重になる必要があります。なぜなら、コントローラ内の new - オブジェクトは、追跡が困難な依存関係を作成し、テスト用にモックするのが困難だからです。 new()
wily-nillyを使うよりあなたのためにオブジェクトのコレクションを管理します。)
また、どちらの場合も、angleはシングルトンの管理に役立ちます。サービスや関数をどこで何回注入しても、同じオブジェクトまたは関数への参照は同じになります。 (ファクトリが単に数値や文字列のような値を返すときを除いて。その場合、あなたは常に同じ値を取得しますが、参照は取得しません。)
簡単に言えば ..
// Service
service = (a, b) => {
a.lastName = b;
return a;
};
// Factory
factory = (a, b) => Object.assign({}, a, { lastName: b });
const fullName = { firstName: 'john' };
// Service
const lastNameService = (a, b) => {
a.lastName = b;
return a;
};
console.log(lastNameService(fullName, 'doe'));
// Factory
const lastNameFactory = (a, b) =>
Object.assign({}, a, { lastName: b })
console.log(lastNameFactory(fullName, 'doe'));
主な違いは次のとおりです。
構文:module.service( 'serviceName', function );
結果:serviceNameを注入可能な引数として宣言すると、module.service
に渡された関数のインスタンスが提供されます。
使用法:注入された関数参照に単に( )
を追加することで呼び出すのに便利な共有ユーティリティ関数に役立ちます。 injectedArg.call( this )
などで実行することもできます。
構文:module.factory( 'factoryName', function );
結果:factoryNameを注入可能な引数として宣言すると、module.factory
に渡される関数参照を呼び出すことによって返される値が提供されます。
使用法:インスタンスを作成するために新規に使用できる'class'関数を返すのに便利です。
これは サービスとファクトリを使った例です 。 AngularJSサービスvsファクトリーについての詳細を読んでください 。
AngularJSのドキュメント やstackoverflowに関する同様の質問もチェックすることができます サービスとファクトリーについて混乱している 。
TL; DR
1) ファクトリを使用している場合 オブジェクトを作成し、それにプロパティを追加してから、同じオブジェクトを返します。このファクトリをコントローラに渡すと、オブジェクトのこれらのプロパティはファクトリを通じてそのコントローラで利用できるようになります。
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.artist = myFactory.getArtist();
});
app.factory('myFactory', function(){
var _artist = 'Shakira';
var service = {};
service.getArtist = function(){
return _artist;
}
return service;
});
2) Service を使用している場合、Angularは「new」キーワードを使用して裏側でインスタンス化します。そのため、「this」にプロパティを追加すると、サービスは「this」を返します。あなたがあなたのコントローラにサービスを渡すとき、「これ」のそれらのプロパティはあなたのサービスを通してそのコントローラで利用可能になります。
app.controller('myServiceCtrl', function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service('myService', function(){
var _artist = 'Nelly';
this.getArtist = function(){
return _artist;
}
});
非TL; DR
1)工場
工場は、サービスを作成および構成するための最も一般的な方法です。 TL、DRが言ったこと以上のものは本当にありません。オブジェクトを作成し、それにプロパティを追加してから、同じオブジェクトを返すだけです。その後、ファクトリをコントローラに渡すと、オブジェクトのこれらのプロパティはファクトリを通じてそのコントローラで利用できるようになります。より広範な例を以下に示します。
app.factory('myFactory', function(){
var service = {};
return service;
});
MyFactoryをコントローラに渡すと、serviceに付加したプロパティはすべて使用できるようになります。
それでは、コールバック関数にいくつかの「非公開」変数を追加しましょう。これらはコントローラから直接アクセスすることはできませんが、必要に応じてこれらの「プライベート」変数を変更できるように、最終的に「service」にゲッター/セッターメソッドを設定します。
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://iTunes.Apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK';
return _finalUrl
}
return service;
});
ここでは、これらの変数や関数を「service」に関連付けていないことに気付くでしょう。後で使用または変更するために、それらを作成するだけです。
これで、ヘルパー/プライベート変数と関数が用意されたので、 'service'オブジェクトにいくつかのプロパティを追加しましょう。私たちが「サービス」を提供するものは何でも、私たちが「myFactory」を渡すどのコントローラでも直接使用することができます。
アーティストを返すか設定するだけのsetArtistおよびgetArtistメソッドを作成します。作成したURLでiTunes APIを呼び出すメソッドも作成します。このメソッドは、データがiTunes APIから戻ってきたときに実行される約束を返します。 Angularでプロミスを使用した経験があまりない場合は、ディープダイビングをすることを強くお勧めします。
Below setArtist はアーティストを受け入れ、アーティストを設定できます。 getArtist は、私たちの$ httpリクエストで使用するURLを作成するために、最初にmakeUrl()を呼び出すアーティストのcallItunesを返します。それからpromiseオブジェクトを設定し、最後のURLで$ httpリクエストを行います。そして$ httpがpromiseを返すので、リクエストの後に.successまたは.errorを呼び出すことができます。その後、私たちはiTunesのデータで約束を解決するか、「エラーがありました」というメッセージでそれを拒否します。
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://iTunes.Apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
service.setArtist = function(artist){
_artist = artist;
}
service.getArtist = function(){
return _artist;
}
service.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
return service;
});
これで私たちの工場は完成しました。これで「myFactory」を任意のコントローラに注入できるようになり、サービスオブジェクトにアタッチしたメソッドを呼び出すことができるようになります(setArtist、getArtist、およびcallItunes)。
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.data = {};
$scope.updateArtist = function(){
myFactory.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myFactory.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
上記のコントローラでは、「myFactory」サービスを注入しています。次に、myFactoryのデータから取得したプロパティを$ scopeオブジェクトに設定します。上記の唯一のトリッキーなコードは、あなたがこれまで約束を扱ったことがない場合です。 callItunesは約束を返しているので、.then()メソッドを使用して、約束がiTunesデータで満たされた後にのみ$ scope.data.artistDataを設定することができます。私たちのコントローラーは非常に薄いです。私たちのロジックと永続データはすべて私たちのサービス内にあり、私たちのコントローラー内にはありません。
2)サービス
おそらく、サービスの作成を扱うときに知っておくべき最大のことは、それが「new」キーワードでインスタンス化されているということです。あなたにとってJavaScriptの達人はこれがあなたにコードの性質への大きなヒントを与えるべきです。 JavaScriptの知識が限られている方、または「new」キーワードが実際に行うことに慣れていない方のために、最終的にサービスの性質を理解するのに役立つJavaScriptの基礎をいくつか見てみましょう。
「new」キーワードを使用して関数を呼び出したときに発生する変更を実際に確認するには、関数を作成して「new」キーワードを使用して呼び出してから、「new」キーワードを見たときのインタプリタの動作を示します。最終結果は両方とも同じになります。
まずコンストラクタを作成しましょう。
var Person = function(name, age){
this.name = name;
this.age = age;
}
これは典型的なJavaScriptコンストラクタ関数です。 「new」キーワードを使用してPerson関数を呼び出すたびに、「this」は新しく作成されたオブジェクトにバインドされます。
それでは、Personのプロトタイプにメソッドを追加して、Personのすべてのインスタンスのクラスで使用できるようにしましょう。
Person.prototype.sayName = function(){
alert('My name is ' + this.name);
}
プロトタイプにsayName関数を追加したので、Personのすべてのインスタンスは、そのインスタンスの名前を警告するためにsayName関数を呼び出すことができます。
プロトタイプにPersonコンストラクター関数とsayName関数があるので、実際にPersonのインスタンスを作成してから、sayName関数を呼び出します。
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'
つまり、Personコンストラクタを作成し、そのプロトタイプに関数を追加し、Personインスタンスを作成してから、そのプロトタイプでその関数を呼び出すためのコードは、すべてこのようになります。
var Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
alert('My name is ' + this.name);
}
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'
それでは、JavaScriptで「new」キーワードを使用したときに実際に何が起きているのかを見てみましょう。この例で「new」を使用した後は、それがオブジェクトであるかのように、「tyler」でメソッド(sayName)を呼び出すことができます。つまり、最初にPersonコンストラクタがオブジェクトを返していることがわかります。それがコード内で確認できるかどうかは関係ありません。次に、sayName関数は直接Personインスタンスではなくプロトタイプにあるため、Person関数が返すオブジェクトは失敗したルックアップでそのプロトタイプに委任する必要があります。もっと簡単に言うと、tyler.sayName()を呼び出すと、インタプリタは「OK、今作成した「tyler」オブジェクトを調べ、sayName関数を見つけて呼び出します」と言います。ちょっと待って、私はここでそれを見ません - 私が見るのは名前と年齢だけです、私はプロトタイプをチェックしましょう。うん、それはプロトタイプの上にあるように見えます、私がそれを呼びましょう。」.
下記は「new」キーワードが実際にJavaScriptで何をしているのかを考えるためのコードです。これは基本的に上の段落のコード例です。私は「インタプリタビュー」、つまりインタプリタがコードの中にコードを見る方法を置きます。
var Person = function(name, age){
//The line below this creates an obj object that will delegate to the person's prototype on failed lookups.
//var obj = Object.create(Person.prototype);
//The line directly below this sets 'this' to the newly created object
//this = obj;
this.name = name;
this.age = age;
//return this;
}
JavaScriptでの 'new'キーワードの実際の動作に関する知識を得たので、Angularでサービスを作成する方が理解しやすくなります。
サービスを作成するときに理解するべき最大のことは、サービスが「new」キーワードでインスタンス化されていることを知っていることです。その知識を上記の例と組み合わせることで、あなたは自分のプロパティとメソッドを「this」に直接付加することになり、それがサービス自体から返されることになります。これを実際に見てみましょう。
Factoryの例で最初に行ったこととは異なり、オブジェクトを作成してからそのオブジェクトを返す必要はありません。これは、前述のように、 'new'キーワードを使用してインタープリタがそのオブジェクトを作成するためです。それはプロトタイプであり、それから私たちがその仕事をする必要なしにそれを私たちのために返します。
まず最初に、私たちの「プライベート」およびヘルパー関数を作成しましょう。私達は私達の工場と全く同じことをしたので、これは非常におなじみのはずです。ここでは各行の動作を説明しません。ファクトリの例で説明したのですが、混乱した場合はファクトリの例をもう一度読んでください。
app.service('myService', function($http, $q){
var baseUrl = 'https://iTunes.Apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
});
これで、コントローラで利用できるようになるすべてのメソッドを「this」に追加します。
app.service('myService', function($http, $q){
var baseUrl = 'https://iTunes.Apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
this.setArtist = function(artist){
_artist = artist;
}
this.getArtist = function(){
return _artist;
}
this.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
});
今や私たちの工場のように、setArtist、getArtist、およびcallItunesはmyServiceを渡したどのコントローラでも利用可能になります。これがmyServiceコントローラーです(これは、ファクトリーコントローラーとほぼ同じです)。
app.controller('myServiceCtrl', function($scope, myService){
$scope.data = {};
$scope.updateArtist = function(){
myService.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myService.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
前述したように、「新しい」機能の概要を理解すると、サービスはAngularの工場とほぼ同じになります。
サービスと工場は互いに似ています。どちらも他のオブジェクトに挿入できるシングルトンオブジェクトを生成するので、しばしば同じ意味で使用されます。
それらは異なるデザインパターンを実装するために意味的に使われることを意図しています。
サービスパターンとは、アプリケーションが論理的に一貫した機能単位に分割されているパターンです。例としては、APIアクセサ、または一連のビジネスロジックがあります。
Angularモデルは通常サーバーから取得した単なるJSONオブジェクトなので、これはAngularで特に重要です。
これは例えばGithubサービスです。それはGithubと話す方法を知っています。それはURLとメソッドについて知っています。私たちはそれをコントローラーに注入することができます、そしてそれは約束を生成して返します。
(function() {
var base = "https://api.github.com";
angular.module('github', [])
.service('githubService', function( $http ) {
this.getEvents: function() {
var url = [
base,
'/events',
'?callback=JSON_CALLBACK'
].join('');
return $http.jsonp(url);
}
});
)();
一方、工場は工場パターンを実行することを目的としています。ファクトリ関数を使用してオブジェクトを生成するファクトリパターン。通常、これをモデルの構築に使用します。これはAuthorコンストラクタを返すファクトリです。
angular.module('user', [])
.factory('User', function($resource) {
var url = 'http://simple-api.herokuapp.com/api/v1/authors/:id'
return $resource(url);
})
これを次のように利用します。
angular.module('app', ['user'])
.controller('authorController', function($scope, User) {
$scope.user = new User();
})
ファクトリもシングルトンを返すことに注意してください。
ファクトリは単にオブジェクトを返すだけなので、上で見たように、コンストラクタ関数を含め、好きなタイプのオブジェクトを返すことができます。
もう1つの技術的な違いは、サービスと工場の構成方法にあります。オブジェクトを生成するためにサービス関数が新しくなります。ファクトリ関数が呼び出されてオブジェクトを返します。
これは、サービスにおいて、コンストラクタの文脈では、構築中のオブジェクトを指す "this"を追加することを意味します。
これを説明するために、サービスとファクトリを使用して作成した同じ単純なオブジェクトを次に示します。
angular.module('app', [])
.service('helloService', function() {
this.sayHello = function() {
return "Hello!";
}
})
.factory('helloFactory', function() {
return {
sayHello: function() {
return "Hello!";
}
}
});
ここでのすべての答えはサービスと工場に関するもののようです、そしてそれはそれが質問されていたものだったのでそれは有効です。しかし、provider()
、value()
、constant()
など、他にもいくつかあることに留意することも重要です。
覚えておくべき重要な点は、それぞれが他方の特別な場合であるということです。連鎖の下にあるそれぞれの特別なケースでは、少ないコードで同じことができます。それぞれに追加の制限もあります。
どちらを使用するかを決定するには、どちらを使用すればよいのかを確認して、必要な作業を少ないコードで実行できます。これは、どれほど似ているかを示す画像です。
段階的な内訳とそれぞれをいつ使用するかについてのクイックリファレンスのための完全なステップについては、私がこの画像を入手したブログ記事をご覧ください。
建設
ファクトリの場合、Angularは関数を呼び出して結果を取得します。それがキャッシュされ注入された結果です。
//factory
var obj = fn();
return obj;
サービスでは、Angularは new を呼び出してコンストラクタ関数を呼び出します。構築された関数はキャッシュされ注入されます。
//service
var obj = new fn();
return obj;
実装
戻り値 は であるため、ファクトリは通常オブジェクトリテラルを返します。これは、コントローラ、実行ブロック、ディレクティブなどに注入されるものです。
app.factory('fn', function(){
var foo = 0;
var bar = 0;
function setFoo(val) {
foo = val;
}
function setBar (val){
bar = val;
}
return {
setFoo: setFoo,
serBar: setBar
}
});
サービス関数は通常何も返しません。代わりに、それらは初期化を実行し、機能を公開します。 'new'を使って構築されているので、関数は 'this'を参照することもできます。
app.service('fn', function () {
var foo = 0;
var bar = 0;
this.setFoo = function (val) {
foo = val;
}
this.setBar = function (val){
bar = val;
}
});
結論
工場やサービスを使用することになると、それらは両方とも非常によく似ています。それらはコントローラ、ディレクティブ、実行ブロックなどに注入され、クライアントコードでもほぼ同じように使用されます。それらはまた両方ともシングルトンです - サービス/ファクトリが注入されるすべての場所の間で同じインスタンスが共有されることを意味します。
それで、あなたはどちらを好むべきですか?どちらか - それらは非常に似ているので違いは些細です。どちらかを選択する場合は、それらがどのように構成されているのかに注意して、正しく実装できるようにしてください。
私はその違いを理解するためにしばらく時間を費やしました。
そして、ファクトリ関数はモジュールパターンを使い、サービス機能は標準のJavaスクリプトコンストラクタパターンを使うと思います。
ファクトリパターンはオブジェクトと同様に関数と値を返すことができるのでより柔軟です。
サービスパターンIMHOにはそれほど重要ではありません。ファクトリを使用しても同じように簡単にできるからです。例外は次のとおりです。
間違いなく、サービスパターンは構文上の観点から新しいオブジェクトを作成するための やや より良い方法ですが、インスタンス化にはよりコストがかかります。他の人たちは、サービスを作成するためにAngularが "new"を使うことを示していますが、これは本当ではありません - すべてのサービスコンストラクタが異なる数のパラメータを持っているのでそれができません。 Angularが実際に行うことは、コンストラクタ関数をラップするためにファクトリパターンを内部的に使用することです。それから、 simulate javascriptの "new"演算子に可変数の注入可能な引数を使ってコンストラクタを呼び出すための巧妙なジグジーポケリーを行います - しかし、ファクトリパターンを直接使用するのであればこのステップは省略できますコードの効率.