angularjs service教學 - angular.service vs angular.factory




controller dependency (9)

  angular.service('myService', myServiceFunction);
  angular.factory('myFactory', myFactoryFunction);

我一直困擾著這個概念,直到我這樣對自己說:

服務 :你寫的功能將是新的

  myInjectedService  <----  new myServiceFunction()

工廠 :您將編寫的函數 (構造函數)將被調用

  myInjectedFactory  <---  myFactoryFunction()

你做什麼取決於你,但有一些有用的模式...

比如編寫一個服務函數來公開一個公共API:

function myServiceFunction() {
  this.awesomeApi = function(optional) {
    // calculate some stuff
    return awesomeListOfValues;
  }
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();

或者使用工廠函數公開一個公共API:

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();

哪一個使用?...

你可以用兩者完成同樣的事情。 但是,在某些情況下, 工廠為您提供更多的靈活性,以更簡單的語法創建注射劑。 這是因為雖然myInjectedService必須始終是一個對象,但myInjectedFactory可以是對象,函數引用或任何值。 例如,如果您編寫了一個服務來創建構造函數(如上面的最後一個示例中所示),它將不得不像下面這樣實例化:

var myShinyNewObject = new myInjectedService.myFunction()

這可以說比這個更不理想:

var myShinyNewObject = new myInjectedFactory();

(但是,首先應該謹慎使用這種類型的模式,因為控制器中的對象會創建難以模擬測試的難以跟踪的依賴關係。更好地讓服務管理一組對象你比使用new()愜意。)

還有一件事,他們都是單身...

還要記住,在這兩種情況下,angular都會幫助你管理一個單身人士。 無論您注入服務或功能的位置或次數如何,您都會得到與同一對像或功能相同的參考。 (除了工廠簡單地返回一個數字或字符串的值之外,在這種情況下,您將始終獲得相同的值,但不是參考。)

我已經看到用於聲明服務的angular.factory()angular.service() ; 但是,我angular.service()在官方文檔中angular.service() angular.service

這兩種方法有什麼區別? 哪些應該用於什麼(假設他們做了不同的事情)?


app.factory('fn',fn)與app.service('fn',fn)

施工

有了工廠,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
         }
  });

服務功能通常不會返回任何內容。 相反,他們執行初始化和公開功能。 函數也可以引用'this',因為它是用'new'構造的。

app.service('fn', function () {
         var foo = 0;
         var bar = 0;
         this.setFoo = function (val) {
               foo = val;
         }
         this.setBar = function (val){
               bar = val;
         }
});

結論

當涉及到使用工廠或服務時,它們都非常相似。 它們被注入到控制器,指令,運行塊等中,並以幾乎相同的方式用於客戶端代碼中。 他們也都是單身人士 - 這意味著所有註入服務/工廠的地方都會共享相同的實例。

那麼你應該選擇哪一個? 任何一個 - 它們都非常相似,以至於差異變得微不足道。 如果您確實選擇了一個,請注意它們是如何構建的,以便您可以正確實施它們。


簡單的說 ..

// 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'));


這裡的所有答案似乎都是圍繞服務和工廠進行的,這是有效的,因為那是被問到的。 但是請記住,還有其他幾個方面,包括provider()value()constant()也很重要。

要記住的關鍵是每一個都是另一個的特例。 鏈中的每個特殊情況都允許您用較少的代碼完成相同的事情。 每個人也有一些額外的限制。

要決定何時使用哪一個,你只要看到哪一個允許你以較少的代碼做你想做的事情。 這是一張圖片,說明它們有多相似:

有關完整的逐步分解以及何時使用每種方法的快速參考,請訪問我從以下位置獲取此映像的博客文章:


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)當你使用服務時 ,Angular用'new'關鍵字在幕後實例化它。 因此,您將添加屬性到'this',服務將返回'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)工廠
工廠是創建和配置服務最流行的方式。 DR說的並不比TL多很多。 您只需創建一個對象,為其添加屬性,然後返回相同的對象。 然後,當您將工廠傳遞到您的控制器時,對像上的這些屬性現在將通過您的工廠在該控制器中可用。 下面是一個更廣泛的例子。

app.factory('myFactory', function(){
  var service = {};
  return service;
});

現在,當我們將'myFactory'傳遞給我們的控制器時,無論我們附加到“服務”的屬性都可用。

現在,讓我們在回調函數中添加一些“私有”變量。 這些將不能直接從控制器訪問,但我們最終將在“服務”上設置一些getter / setter方法,以便在需要時更改這些“私有”變量。

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;
});

這裡您會注意到我們沒有將這些變量/函數附加到“服務”上。 我們只是創建它們以便稍後使用或修改它們。

  • baseUrl是iTunes API所需的基本URL
  • _artist是我們希望查找的藝術家
  • _finalUrl是我們打電話給iTunes的最終完整構建的URL makeUrl是一個函數,它將創建並返回我們的iTunes友好的URL。

現在我們的助手/私有變量和函數已經到位了,讓我們為'service'對象添加一些屬性。 無論我們放在'服務'上,我們都可以直接使用'myFactory'傳遞到的任何控制器。

我們將創建setArtist和getArtist方法,以簡單地返回或設置藝術家。 我們還將創建一個方法,用我們創建的URL調用iTunes API。 這種方法將返回一旦數據從iTunes API返回就會實現的承諾。 如果您在Angular中沒有很多使用承諾的經驗,我強烈建議您深入了解它們。

setArtist下面接受一位藝術家並允許您設置藝術家。 getArtist返回藝術家callItunes首先調用makeUrl()以構建我們將用於$ http請求的URL。 然後,它建立一個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有限背景的人,或者對那些對'新'關鍵字實際上並不太熟悉的人來說,我們來回顧一些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()時,解釋器會說:“好的,我將查看剛創建的'tyler'對象,找到sayName函數,然後調用它。 等一下,我沒看到它 - 我看到的只是名字和年齡,讓我檢查原型。 是的,看起來像是在原型上,讓我稱之為。“

以下是關於如何考慮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;
}

現在掌握了關於'new'關鍵字在JavaScript中確實做了什麼的知識,創建一個Service in Angular應該更容易理解。

在創建服務時要理解的最重要的事情是知道服務使用“新”關鍵字實例化。 將這些知識與上面的示例結合起來,您現在應該認識到,您將直接將您的屬性和方法附加到“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控制器(與我們的工廠控制器幾乎完全相同)。

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的工廠一樣。


我花了一些時間試圖找出差異。

我認為工廠函數使用模塊模式和服務函數使用標準的Java腳本構造器模式。


主要區別如下:

服務

語法: module.service( 'serviceName', function );

結果:將serviceName聲明為註入參數時,將為您提供傳遞給module.service 的函數實例

用法:對於通過簡單地將( )添加到注入的函數引用來調用可用的共享實用程序函數很有用。 也可以運行injectedArg.call( this )或類似的。

工廠

語法: module.factory( 'factoryName', function );

結果:將factoryName聲明為註入參數時,將提供通過調用傳遞給module.factory 的函數引用返回

用法:可以用於返回可用於創建實例的'class'函數。

這裡是使用服務和工廠的例子 。 閱讀更多關於AngularJS服務與工廠的信息

你也可以檢查關於服務vs工廠的關於的angular.factory()和類似的問題。


工廠模式更加靈活,因為它可以返回函數和值以及對象。

在服務模式恕我直言,沒有多少點,因為它所做的一切,你可以輕鬆地做一個工廠。 例外可能是:

  • 如果您關心實例化服務的聲明類型 - 如果使用服務模式,那麼您的構造函數將成為新服務的類型。
  • 如果你已經有了一個你正在其他地方使用的構造函數,那麼你也想作為一個服務使用(儘管如果你想向它注入任何東西,可能沒有多大用處)。

可以說,從語法的角度來看,服務模式是一種稍微好一點的創建新對象的方式,但實例化的成本也更高。 其他人表示角度使用“新”來創建服務,但這不是真的 - 它不能這樣做,因為每個服務構造函數都有不同數量的參數。 實際上,角度做的是在內部使用工廠模式來包裝您的構造函數。 然後它做了一些巧妙的jiggery pokery來模擬 javascript的“new”運算符,用可變數量的可注入參數調用您的構造函數 - 但如果您直接使用工廠模式,則可以省略此步驟,因此可以非常輕微地提高您的效率碼。


有三種方法可以做到這一點,

a)使用服務

b)利用控制器範圍之間的父母/子女關係。

c)在Angular 2.0中,“As”關鍵字將把數據從一個控制器傳遞到另一個控制器。

有關示例的更多信息,請查看以下鏈接:

http://www.learnit.net.in/2016/03/angular-js.html





angularjs angular-services