摘要:首先創建我們的構造函數這是一個典型的構造函數。所以,我們首先知道的就是無論我們是否能夠在代碼里面看見,構造函數是會返回一個對象的。
翻譯自 http://tylermcginnis.com/angularjs-factory-vs-service-vs-provider/
當你開始使用Angular的時候,你會發現,你總是會讓你的控制器和作用域充滿各種不必要的邏輯。你應該早點意識到一個控制器應該是很簡潔精煉的;同時大多數的商業邏輯和一些重復性的數據都應該要存儲到服務中。一天我在Stack Overflow上看到一些問題說是考慮將重復性的數據放在控制器里,但是,這不是這不是一個控制器應該有的目的。如果為了內存需要,控制器就應該在需要他們的時候實例化,在不需要的時候就取消掉。因此,Angular在你每次切換路由的時候,就會清理當前的控制器。但是呢,服務為我們提供了一種長期存儲應用數據的方式,同時,也可以在不同的控制器之間統一的使用服務。
Angular為我們提供了三種創建服務的方式:
1、Factory
2、Service
3、Provider
一、當使用factory來創建服務的時候,相當于新創建了一個對象,然后在這個對象上新添屬性,最后返回這個對象。當把這個服務注入控制器的時候,控制器就可以訪問在那個對象上的屬性了。
app.factory("MyFactory", function () { var _artist = "", service = {}; service.getArtist = function () { return _artist; }; return service; }) .controller("myFactoryCtrl", [ "$scope", "MyFactory", function ( $scope, MyFactory ) { $scope.artist = MyFactory.getArtist(); }]);
二、當使用service創建服務的時候,相當于使用new關鍵詞進行了實例化。因此,你只需要在this上添加屬性和方法,然后,服務就會自動的返回this。當把這個服務注入控制器的時候,控制器就可以訪問在那個對象上的屬性了。
app.service("MyService", function () { var _artist = ""; this.getArtist = function () { return _artist; }; }) .controller("myServiceCtrl", [ "$scope", "MyService", function ( $scope, MyService ) { $scope.artist = MyService.getArtist(); }]);
三、provider是唯一一種可以創建用來注入到config()函數的服務的方式。想在你的服務啟動之前,進行一些模塊化的配置的話,就使用provider。
app.provider("MyProvider", function () { // 只有直接添加在this上的屬性才能被config函數訪問 this._artist = ""; this.thingFromConfig = ""; // 只有$get函數返回的屬性才能被控制器訪問 this.$get = function () { var that = this; return { getArtist: function () { return that._artist; }, thingFromConfig: that.thingFromConfig }; }; }) .config(["MyProvider", function ( MyProvider ) { MyProvider.thingFormConfig = "this is set in config()"; }]) .controller("myProviderCtrl", [ "$scope", "MyProvider", function ( $scope, MyProvider ) { $scope.artist = MyProvider.getArtist(); }]);下面我們來詳細說明
為了詳細的說明這三種方式的不同之處,我們分別使用這三種方式來創建同一個服務。這個服務將會用到iTunes API以及promise的$q。
使用factory
要創建和配置服務,最普通的做法就是使用factory。就像上面簡單說明的那樣,這里也沒有太多要說明的地方,就是創建一個對象,然后為他添加屬性和方法,最后返回這個對象。當把這個服務注入控制器的時候,控制器就可以訪問在那個對象上的屬性了。一個很普通的例子就像下面那樣。
首先我們創建一個對象,然后返回這個對象。
app.factory("MyFactory", function () { var service = {}; return service; });
現在,我們添加到service上的任何屬性,只要將MyFactory注入到控制器,控制器就都可以訪問了。
現在,我們添加一些私有屬性到回調函數里,雖然不能從控制器里直接訪問這些變量,但是最終我們會提供一些getter和setter方法到service上以便于我們在需要的時候修改這些屬性。
app.factory("MyFactory", [ "$http", "$q", function ( $http, $q ) { var service = {}, baseUrl = "https://itunes.apple.com/search?term=", _artist = "", _finalUrl = ""; function makeUrl() { _artist = _artist.split(" ").join("+"); _finalUrl = baseUrl + _artist + "&callback=JSON_CALLBACK"; return _finalUrl; } return service; }]);
你應該注意到了,我們沒有把這些屬性和方法添加到service對象上去。我們現在只是先簡單的創建出來,以便于待會兒使用或者修改。
baseUrl是iTunes API需要的基本URL
_artist是我們需要查找的藝術家
_finalUrl是最終向iTunes發送請求的URL
makeUrl是一個用來創建返回我們最終的URL的函數
既然我們的輔助變量和函數都創建好了,那么,就往service添加一些屬性吧。我們在service上添加的任何屬性,只要服務注入了控制器中,那么,控制器就可以訪問這些屬性。
我們要創建一個setArtist()和getArtist()函數來設置以及取得藝術家的值。同時,也要創建一個用于向iTunes發送請求的函數。這個函數會返回一個promise對象,當有數據從iTunes返回的時候,這個promise對象就會執行。如果你對Angular的promise對象還不是很了解的話,推薦你去深入了解一下。
setArtist()接受一個參數并且允許用來設置藝術家的值
getArtist()返回藝術家的值
callITunes()首先會調用makeUrl()函數來創建我們需要使用$http進行請求的URL,然后使用我們最終的URL來發送請求,創建一個promise對象。由于$http返回了promise對象,我們就可以在請求之后調用.success和.error了。然后我們處理從iTunes返回的數據或者駁回,并返回一個錯誤消息,比如There was an error。
app.factory("MyFactory", [ "$http", "$q", function ( $http, $q ) { var service = {}, baseUrl = "https://itunes.apple.com/search?term=", _artist = "", _finalUrl = ""; function makeUrl() { _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 () { var deferred = $q.defer(); _finalUrl = makeUrl(); $http({ method: "JSONP", url: _finalUrl }).success(function ( data ) { deferred.resolve(data); }).error(function ( error ) { deferred.reject(error); }); return deferred.promise; }; return service; }]);
現在,我們的服務就完成了,我們可以將這個服務注入到任何的控制器了,并且,可以使用我們添加到service上的那些方法了(getArtist, setArtise, callITunes)。
app.controller("myFactoryCtrl", [ "$scope", "MyFactory", 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 ( error ) { alert(error); }); }; }]);
在上面的控制器中我們注入了MyFactory服務,然后,將從服務里來的數據設置到$scope的屬性上。上面的代碼中最難的地方應該就是你從來沒有使用過promise。由于callITunes()返回了一個promise對象,所以一旦有數據從iTunes返回,promise執行的時候,我們就可以使用.then()方法來設置$scope.data.artistData的值了。你會注意到,我們的控制器非常簡潔,我們所有的邏輯和重復性數據都寫在了服務里面。
使用service
也許在使用service創建服務時,我們需要知道的最重要的一件事就是他是使用new關鍵字進行實例化的。如果你是Javascript大師,你應該知道從代碼的本質來思考。對于那些不了解Javascript背景的或者并不熟悉new實際做了什么的程序員,我們需要復習一下Javascript的基礎知識,以便于最終幫助我們理解service的本質。
為了真正的看到當我們使用new來調用函數的時候發生了什么,我們來創建一個函數,并且使用new來調用他,然后,我們再看看在解釋器發現new的時候,他會做什么。最終結果肯定是一樣的。
首先創建我們的構造函數:
function Person( name, age ) { this.name = name; this.age = age; }
這是一個典型的構造函數。現在,無論我們什么時候使用new來調用這個函數,this都會被綁定到新創建的那個對象上。
現在我們再在Person的原型上創建一個方法,以便于每一個實例都可以訪問到。
Person.prototype.sayName = function () { alert("My name is: " + this.name); };
現在,由于我們在Person對象的原型上創建了sayName函數,所以,Person的每一個實例都可以調用到這個方法。
既然我們已經有了構造函數和原型方法,那么,就來真正的創建一個Person的實例并且調用sayName函數:
var tyler = new Person("Tyler", 23); tyler.sayName();
所以,最終,所有的代碼合起來就是下面這個樣子:
function Person( 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();
現在我們來看看在使用new的時候到底發生了什么。首先你應該注意到的是,在我們的例子中,使用了new之后,我們可以使用tyler來調用sayName方法,就好像這是一個對象一樣,當然,tyler確實是一個對象。所以,我們首先知道的就是無論我們是否能夠在代碼里面看見,Person構造函數是會返回一個對象的。第二,我們我們應該知道,sayName方法是在原型上的,不是直接定義在Person對象實例上的,所以,Person返回的對象必須是通過原型委托的。用更簡單的例子說就是,當我們調用tyler.sayName()的時候,解釋器就會說:“OK,我將會在剛創建的tyler對象上查找sayName函數,然后調用他。等會兒,我沒有發現這個函數,只看到了name和age屬性,讓我再檢查一下原型。哦,原來在原型上,讓我來調用他”。
下面的代碼就是你能夠想象的在Javascript里,new實際做了什么。下面的代碼是一個很基礎的例子,我以解釋器的視角來添加了一些注釋:
function Person( name, age ) { //var obj = object.create(Person.prototype); //this = obj; this.name = name; this.age = age; //return thisl }
現在,既然知道了new做了什么,那么,使用service來創建服務也很容易理解了。
在使用service創建服務時,我們需要知道的最重要的一件事就是他是使用new關鍵字進行實例化的。與上面的例子的知識相結合,你應該就能意識到你要把屬性和方法添加到this上,并且,服務會自動返回this。
與我們使用factory創建服務的方式不同,我們不需要新創建一個對象然后再返回這個對象,因為正如我們前面所提到的那樣,我們使用new的時候,解釋器會自動創建對象,并且代理到他的原型,然后代替我們返回。
所以,在所有的開始之前,我們先創建我們的私有輔助函數,與我們之前使用factory創建的時候非常類似。現在我不會解釋每一行的意義了,如果你有什么疑惑的話,可以看看前面的factory的例子。
app.service("MyService", [ "$http", "$q", function ( $http, $q ) { var baseUrl = "https://itunes.apple.com/search?term=", _artist = "", _finalUrl = ""; function makeUrl() { _artist = _artist.split(" ").join("+"); _finalUrl = baseUrl + _artist + "&callback=JSON_CALLBACK"; return _finalUrl; } }]);
現在,我們會把可用的方法都添加到this上。
app.service("MyService", [ "$http", "$q", function ( $http, $q ) { var baseUrl = "https://itunes.apple.com/search?term=", _artist = "", _finalUrl = ""; function makeUrl() { _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 () { var deferred = $q.defer(); _finalUrl = makeUrl(); $http({ method: "JSONP", url: _finalUrl }).success(function ( data ) { deferred.resolve(data); }).error(function ( error ) { deferred.reject(error); }); return deferred.promise; }; }]);
現在,就像我們使用factory所創建的服務那樣,注入這個服務的任何一個控制器都可以使用setArtist,getArtist和callITunes方法了。下面是我們的myServiceCtrl,幾乎與myFactoryCtrl相同。
app.controller("myServiceCtrl", [ "$scope", "MyService", 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 ( error ) { alert(error); }); }; }]);
正如我之前提到的,一旦你理解了new關鍵詞做了什么,service和factory就幾乎是相同的。
使用provider
關于provider,要記住的最重要的一件事就是他是唯一一種可以創建用來注入到app.config()函數的服務的方式。
如果你需要在你的應用在別處運行之前對你的服務對象進行一部分的配置,那么,這個就顯得很重要了。盡管與service和provider類似,但是我們還是會講解一些他們的不同之處。
首先,類似的,我們設置我們的provider。下面的變量就是我們的私有函數。
app.provider("MyProvider", function () { var baseUrl = "https://itunes.apple.com/search?term=", _artist = "", _finalUrl = ""; // 從config函數里設置這個屬性 this.thingFromConfig = ""; function makeUrl() { _artist = _artist.split(" ").join("+"); _finalUrl = baseUrl + _artist + "&callback=JSON_CALLBACK"; return _finalUrl; } });
再說明一次,如果對上面的代碼邏輯有疑問的話,可以參考之前的列子。
你可以認為provider有三個部分,第一部分是私有變量和私有函數,這些變量和函數會在以后被修改。第二部分是在app.config函數里可以訪問的變量和函數,所以,他們可以在在其他地方使用之前被修改。注意,這些變量和函數一定要添加到this上面才行。在我們的例子中,app.config()函數能夠修改的只有thingFromConfig。第三部分是在控制器里可以訪問的變量和函數。
當使用 provider創建服務的時候,唯一可以讓控制器訪問的屬性和方法是在$get()函數里返回的屬性和方法。下面的代碼將$get添加到了this上面,最終這個函數會被返回。
現在,$get()函數會返回我們需要在控制器里訪問的函數和變量。下面是代碼例子:
this.$get = function ( $http, $q ) { return { setArtist: function ( artist ) { _artist = artist; }, getArtist: function () { return _artist; }, callITunes: function () { var deferred = $q.defer(); _finalUrl = makeUrl(); $http({ method: "JSONP", url: _finalUrl }).success(function ( data ) { deferred.resolve(data); }).error(function ( error ) { deferred.reject(error); }); return deferred.promise; }, thingOnConfig: this.thingFromConfig }; };
現在,完整的provider就是這個樣子:
app.provider("MyProvider", [ "$http", "$q", function ( $http, $q ) { var baseUrl = "https://itunes.apple.com/search?term=", _artist = "", _finalUrl = ""; this.thingFromConfig = ""; this.$get = function ( $http, $q ) { return { setArtist: function ( artist ) { _artist = artist; }, getArtist: function () { return _artist; }, callITunes: function () { var deferred = $q.defer(); _finalUrl = makeUrl(); $http({ method: "JSONP", url: _finalUrl }).success(function ( data ) { deferred.resolve(data); }).error(function ( error ) { deferred.reject(error); }); return deferred.promise; }, thingOnConfig: this.thingFromConfig }; }; function makeUrl() { _artist = _artist.split(" ").join("+"); _finalUrl = baseUrl + _artist + "&callback=JSON_CALLBACK"; return _finalUrl; } }]);
現在,與之前的service和factory類似,只要我們把MyProvider注入到控制器里面,對應的方法就可以使用了。下面是myProviderCtrl。
app.controller("myProviderCtrl", [ "$scope", "MyProvider", function ( $scope, MyProvider ) { $scope.data = {}; $scope.updateArtist = function () { MyProvider.setArtist($scope.data.artist); }; $scope.submitArtist = function () { MyProvider.callITunes().then(function ( data ) { $scope.data.artistData = data; }, function ( error ) { alert(error); }); }; $scope.data.thingFromConfig = MyProvider.thingOnConfig; }]);
正如之前提到的,使用provider來創建服務的目的就是為了能夠通過app.config()函數修改一些變量來傳遞到最終的項目中。我們來看個例子:
app.config(["MyProviderProvider", function ( MyProviderProvider ) { MyProviderProvider.thingFromConfig = "This sentence was set in app.config. Providers are the only service that can be passed into app.config. Check out the code to see how it works."; }]);
現在,你就能看到,在provider里,thingFromConfig是空字符串,但是,當我們在DOM里顯示的時候,他就會是我們上面所設置的字符串了。
謝謝你的閱讀,希望能夠幫助你分辨這三者的不同之處。
要查看完整的代碼例子,歡迎fork我的項目:https://github.com/tylermcginnis33/AngularServices 或者查看我在Stack Overflow的問題回答
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78896.html
摘要:代碼代碼功夫瑜伽語法糖功夫瑜伽它是一個可注入的構造器在中它是單例的用它在中通信或者共享數據都合適功夫瑜伽語法糖功夫瑜伽注意在里面可以不用返回東西因為會調用關鍵字來創建對象。 AngularJS 的供應商($provide) $provide 服務負責告訴 AngularJS 如何創建一個新的可注入的東西: 即服務。 服務會被叫做供應商的東西來定義, 可以使用 $provide 來創建...
摘要:引言看了很多文章可能還是不太說得出中的幾個創建供應商的方法到底有啥區別,啥時候該用啥,之前一直傻傻分不清楚,現在來總結一下。 引言 看了很多文章可能還是不太說得出AngularJS中的幾個創建供應商(provider)的方法(factory(),service(),provider())到底有啥區別,啥時候該用啥,之前一直傻傻分不清楚,現在來總結一下。 下文中泛指統一用中文,英文即為特...
摘要:同名模塊已經初始化的模塊保存在一個叫的緩存對象中,是模塊名,是模塊對象。調用注入器的方法執行模塊的所有方法。檢查該注入器中是否存在指定的服務。如果是數組,最后一個必須是的構造函數,前面的就是構造函數的參數名。 模塊 模塊是指寫Angular應用的代碼片段,這樣可以使代碼分離開來,因此代碼會更好維護,可讀和測試。還可以在module里定義代碼依賴關系,可以調用一個模塊,再在代碼中定義這個...
閱讀 3012·2021-11-22 12:06
閱讀 599·2021-09-03 10:29
閱讀 6526·2021-09-02 09:52
閱讀 2013·2019-08-30 15:52
閱讀 3411·2019-08-29 16:39
閱讀 1190·2019-08-29 15:35
閱讀 2061·2019-08-29 15:17
閱讀 1416·2019-08-29 11:17