Service
、Provider
和Factory
有什么区别?Service
、Provider
和Factory
有什么区别?这对于新手来说非常令人困惑,我尝试用简单的语言澄清。
AngularJS Service:用于在控制器中使用服务引用共享实用函数。 Service在本质上是单例的,因此在浏览器中只会创建一个实例,并且在整个页面中使用相同的引用。
在服务中,我们将函数名称作为带有this对象的属性。
AngularJS Factory:工厂的目的与服务相同,但在这种情况下,我们创建一个新对象,并将函数作为该对象的属性添加,最后返回该对象。
AngularJS Provider:其目的也是相同的,但Provider返回其$ get函数的输出。
定义和使用服务、工厂和提供程序在http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider中有详细说明。
对我来说,理解这两者的最好且最简单的方式是:
var service, factory;
service = factory = function(injection) {}
AngularJS 实例化特定组件的方式(简化):
// service
var angularService = new service(injection);
// factory
var angularFactory = factory(injection);
因此,对于服务而言,AngularJS组件是由服务声明函数所表示的类的对象实例。对于工厂而言,它是由工厂声明函数返回的结果。工厂可能与服务具有相同的行为:
var factoryAsService = function(injection) {
return new function(injection) {
// Service content
}
}
最简单的思考方式如下:
工厂“类”的示例在注释中提供,以及提供者的不同之处。
new MyService()
这样的语法 :) - joe我的澄清:
基本上所有提到的类型(service、factory、provider等)都只是创建和配置全局变量(当然,这些全局变量对整个应用程序都是全局的),就像老式的全局变量一样。
尽管不推荐使用全局变量,但这些全局变量的真正用途是提供依赖注入,通过将变量传递给相关控制器。
创建“全局变量”的值存在许多复杂级别:
app.config
中使用它们。
app.config
文件设置上述属性后由AngularJS执行,该$.get函数的行为与上面的factory相同,其返回值用于初始化“全局”变量。 我的理解如下:
工厂模式: 您只需在工厂内创建一个对象并返回它。
服务模式:
您只需使用此关键字定义一个函数的标准函数。
提供者模式:
您定义了一个$get
对象,可以用它来获取返回数据的对象。
从Angular文档总结:
来自SO的最佳答案:
https://dev59.com/XWYr5IYBdhLWcg3wVYwg#26924234(<-- 精华)
https://dev59.com/22Mk5IYBdhLWcg3w0RGU#27263882
https://dev59.com/bWQn5IYBdhLWcg3wzZtU#16566144
所有好的答案都已经被提到了。我想再加几点关于Service和Factory的区别,以及它们之间的差异。同时,也有一些问题:
让我们从服务和工厂之间的区别开始:
两者都是单例模式:当Angular第一次发现这些依赖时,它会创建一个服务/工厂的单个实例。一旦实例被创建,同一个实例将永久使用。
可以用来建模带有行为的对象:它们都可以拥有方法、内部状态变量等等。虽然编写代码的方式会有所不同。
服务:
服务是一个构造函数,Angular将通过调用new yourServiceName()
来实例化它。这意味着几件事情。
this
的属性。new yourServiceName()
时,它将接收到带有您放在其中所有属性的this
对象。示例:
angular.service('MyService', function() {
this.aServiceVariable = "Ved Prakash"
this.aServiceMethod = function() {
return //code
};
});
当Angular将
MyService
服务注入到依赖它的控制器中时,该控制器将获得一个可调用函数的MyService
,例如MyService.aServiceMethod ()
。
注意this
关键字:
由于构造的服务是一个对象,因此在调用其内部方法时,这些方法可以引用this
关键字:
angular.service('ScoreKeeper', function($http) {
this.score = 0;
this.getScore = function() {
return this.score;
};
this.setScore = function(newScore) {
this.score = newScore;
};
this.addOne = function() {
this.score++;
};
});
你可能会想在promise链中调用ScoreKeeper.setScore
,比如如果你通过从服务器获取得分来初始化分数:$http.get('/score').then(ScoreKeeper.setScore).
但是这样做有问题,因为ScoreKeeper.setScore
将被绑定到null
,你会得到错误。更好的方法是 $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper))
。
从Service
返回值:
由于JavaScript构造函数的工作方式,如果从constructor
函数中返回一个复杂值(即对象),调用者将获得该对象而不是实例本身。
这意味着你基本上可以复制下面的工厂示例,将factory
替换为service
,它就能够工作:
angular.service('MyService', function($http) {
var api = {};
api.aServiceMethod= function() {
return $http.get('/users');
};
return api;
});
因此,当Angular使用new MyService()构建您的服务时,它将获得该api对象,而不是MyService实例。
这适用于任何复杂值(对象、函数),但不适用于原始类型。
工厂:
工厂是一个普通的函数,它返回一个值。返回值是注入到依赖该工厂的组件或服务中的。在Angular中,一个典型的工厂模式是返回一个具有函数作为属性的对象,就像这样:
angular.factory('MyFactory', function($http) {
var api = {};
api.aFactoryMethod= function() {
return $http.get('/users');
};
return api;
});
工厂依赖项的注入值是工厂的返回值,它不一定是对象,也可以是函数。
上述第1和第2个问题的答案:
在大多数情况下,只需使用工厂即可。它们的行为更容易理解。不需要选择是否返回值,此外,如果您做错了事情,也不会引入错误。
当我谈论将它们作为依赖项注入时,我仍将它们称为“服务”。
服务/工厂的行为非常相似,有些人会说两者都可以。这有点真实,但我发现遵循John Papa风格指南的建议并坚持使用工厂更容易。
需要进一步澄清的是,工厂可以创建函数/基元类型,而服务则不能。请查看基于Epokk的这个jsFiddle:http://jsfiddle.net/skeller88/PxdSP/1351/.
工厂返回一个可以调用的函数:
myApp.factory('helloWorldFromFactory', function() {
return function() {
return "Hello, World!";
};
});
该工厂还可以返回一个带有可调用方法的对象:
myApp.factory('helloWorldFromFactory', function() {
return {
sayHello: function() {
return "Hello, World!";
}
};
});
该服务返回一个带有可调用方法的对象:
myApp.service('helloWorldFromService', function() {
this.sayHello = function() {
return "Hello, World!";
};
});
有关详细信息,请参阅我在以下链接中撰写的文章:http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/
service
(单例对象)的方式/方法,它应该由 $injector(AngulaJS 中的 IoC 模式实现方式)注入。
而Value、Factory、Service 和 Constant(4 种方式)则是在 Provider 方式/方法上的语法糖。Service vs Factory
部分:
https://www.youtube.com/watch?v=BLzNCkPn3ao
Service 实际上都和 new
关键字有关,我们知道它可以做 4 件事情:prototype
对象。
3. 将 context
连接到 this
。
4. 返回 this
。阅读了所有这些帖子后,我更加困惑了。但是所有的信息都很有价值。最终我发现了以下表格,它可以通过简单的比较提供信息:
对于初学者:这可能不是正确的用例,但在高级别上,这就是这三个用例的使用情况。
angular.module('myApp').config(function($testProvider){
$testProvider.someFunction();
})
在基本情况下,工厂和服务的行为相同。
以下是我为AngularJS中的对象工厂编写的代码模板。我使用了汽车/汽车工厂作为示例来说明。这使得在控制器中实现代码变得简单。
<script>
angular.module('app', [])
.factory('CarFactory', function() {
/**
* BroilerPlate Object Instance Factory Definition / Example
*/
this.Car = function() {
// initialize instance properties
angular.extend(this, {
color : null,
numberOfDoors : null,
hasFancyRadio : null,
hasLeatherSeats : null
});
// generic setter (with optional default value)
this.set = function(key, value, defaultValue, allowUndefined) {
// by default,
if (typeof allowUndefined === 'undefined') {
// we don't allow setter to accept "undefined" as a value
allowUndefined = false;
}
// if we do not allow undefined values, and..
if (!allowUndefined) {
// if an undefined value was passed in
if (value === undefined) {
// and a default value was specified
if (defaultValue !== undefined) {
// use the specified default value
value = defaultValue;
} else {
// otherwise use the class.prototype.defaults value
value = this.defaults[key];
} // end if/else
} // end if
} // end if
// update
this[key] = value;
// return reference to this object (fluent)
return this;
}; // end this.set()
}; // end this.Car class definition
// instance properties default values
this.Car.prototype.defaults = {
color: 'yellow',
numberOfDoors: 2,
hasLeatherSeats: null,
hasFancyRadio: false
};
// instance factory method / constructor
this.Car.prototype.instance = function(params) {
return new
this.constructor()
.set('color', params.color)
.set('numberOfDoors', params.numberOfDoors)
.set('hasFancyRadio', params.hasFancyRadio)
.set('hasLeatherSeats', params.hasLeatherSeats)
;
};
return new this.Car();
}) // end Factory Definition
.controller('testCtrl', function($scope, CarFactory) {
window.testCtrl = $scope;
// first car, is red, uses class default for:
// numberOfDoors, and hasLeatherSeats
$scope.car1 = CarFactory
.instance({
color: 'red'
})
;
// second car, is blue, has 3 doors,
// uses class default for hasLeatherSeats
$scope.car2 = CarFactory
.instance({
color: 'blue',
numberOfDoors: 3
})
;
// third car, has 4 doors, uses class default for
// color and hasLeatherSeats
$scope.car3 = CarFactory
.instance({
numberOfDoors: 4
})
;
// sets an undefined variable for 'hasFancyRadio',
// explicitly defines "true" as default when value is undefined
$scope.hasFancyRadio = undefined;
$scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);
// fourth car, purple, 4 doors,
// uses class default for hasLeatherSeats
$scope.car4 = CarFactory
.instance({
color: 'purple',
numberOfDoors: 4
});
// and then explicitly sets hasLeatherSeats to undefined
$scope.hasLeatherSeats = undefined;
$scope.car4.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);
// in console, type window.testCtrl to see the resulting objects
});
</script>
这里有一个更简单的例子。我使用了一些第三方库,它们期望一个“位置”对象来公开纬度和经度,但是通过不同的对象属性。我不想篡改供应商代码,所以我调整了我正在传递的“位置”对象。
angular.module('app')
.factory('PositionFactory', function() {
/**
* BroilerPlate Object Instance Factory Definition / Example
*/
this.Position = function() {
// initialize instance properties
// (multiple properties to satisfy multiple external interface contracts)
angular.extend(this, {
lat : null,
lon : null,
latitude : null,
longitude : null,
coords: {
latitude: null,
longitude: null
}
});
this.setLatitude = function(latitude) {
this.latitude = latitude;
this.lat = latitude;
this.coords.latitude = latitude;
return this;
};
this.setLongitude = function(longitude) {
this.longitude = longitude;
this.lon = longitude;
this.coords.longitude = longitude;
return this;
};
}; // end class definition
// instance factory method / constructor
this.Position.prototype.instance = function(params) {
return new
this.constructor()
.setLatitude(params.latitude)
.setLongitude(params.longitude)
;
};
return new this.Position();
}) // end Factory Definition
.controller('testCtrl', function($scope, PositionFactory) {
$scope.position1 = PositionFactory.instance({latitude: 39, longitude: 42.3123});
$scope.position2 = PositionFactory.instance({latitude: 39, longitude: 42.3333});
}) // end controller
;
service.factory
成功处理。我不想进一步复杂化这个主题。 - demisxservices
、factories
和providers
的好答案。 - Mistalis