据我了解,在工厂内部时,我返回一个注入控制器的对象。在服务内部时,我正在使用 this
处理对象并且不返回任何内容。
我假设一个服务总是一个单例,并且一个新的工厂对象被注入到每个控制器中。然而,事实证明,工厂对象也是单例吗?
示例代码来演示:
var factories = angular.module('app.factories', []);
var app = angular.module('app', ['ngResource', 'app.factories']);
factories.factory('User', function () {
return {
first: 'John',
last: 'Doe'
};
});
app.controller('ACtrl', function($scope, User) {
$scope.user = User;
});
app.controller('BCtrl', function($scope, User) {
$scope.user = User;
});
当更改 ACtrl
中的 user.first
时,结果发现 BCtrl
中的 user.first
也发生了更改,例如 User
是单例?
我的假设是在带有工厂的控制器中注入了一个新实例?
所有角度服务都是单例:
文档(请参阅单例服务):https://docs.angularjs.org/guide/services
最后,重要的是要认识到所有 Angular 服务都是应用程序单例。这意味着每个注入器只有一个给定服务的实例。
基本上服务和工厂的区别如下:
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 + "!";
}
}
});
查看有关 $provide 的演示文稿:http://slides.wesalvaro.com/20121113/#/
这些幻灯片用于 AngularJs 聚会之一:http://blog.angularjs.org/2012/11/more-angularjs-meetup-videos.html
对我来说,当我意识到它们都以相同的方式工作时,我得到了启示:运行一次,存储它们获得的值,然后在通过依赖注入引用时咳出相同的存储值。
假设我们有:
app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);
三者的区别在于:
的存储值来自于运行 fn,换句话说:fn() b 的存储值来自于 newing fn,换句话说:new fn() c 的存储值来自于首先通过 newing fn 获取实例,然后运行 $get实例方法
这意味着,在 angular 内部有一个类似于缓存对象的东西,每次注入的值只分配一次,当它们第一次被注入时,并且在哪里:
cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()
这就是我们在服务中使用 this
并在提供程序中定义 this.$get
的原因。
希望这可以帮助。
.service
和 .factory
都是 .provide
的快捷方式。最后,所有服务都来自调用 .$get()
。
活生生的例子
“你好世界”的例子
与 factory
/ service
/ provider
:
var myApp = angular.module('myApp', []);
//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
this.sayHello = function() {
return "Hello, World!"
};
});
//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
return {
sayHello: function() {
return "Hello, World!"
}
};
});
//provider style, full blown, configurable version
myApp.provider('helloWorld', function() {
// In the provider function, you cannot inject any
// service or factory. This can only be done at the
// "$get" method.
this.name = 'Default';
this.$get = function() {
var name = this.name;
return {
sayHello: function() {
return "Hello, " + name + "!"
}
}
};
this.setName = function(name) {
this.name = name;
};
});
//hey, we can configure a provider!
myApp.config(function(helloWorldProvider){
helloWorldProvider.setName('World');
});
function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {
$scope.hellos = [
helloWorld.sayHello(),
helloWorldFromFactory.sayHello(),
helloWorldFromService.sayHello()];
}
还有一种方法可以返回构造函数,这样您就可以在工厂中返回可更新的类,如下所示:
function MyObjectWithParam($rootScope, name) {
this.$rootScope = $rootScope;
this.name = name;
}
MyObjectWithParam.prototype.getText = function () {
return this.name;
};
App.factory('MyObjectWithParam', function ($injector) {
return function(name) {
return $injector.instantiate(MyObjectWithParam,{ name: name });
};
});
因此,您可以在使用 MyObjectWithParam 的控制器中执行此操作:
var obj = new MyObjectWithParam("hello"),
请参阅此处的完整示例:
http://plnkr.co/edit/GKnhIN?p=preview
这里是 google 群组页面,讨论的地方:
https://groups.google.com/forum/#!msg/angular/56sdORWEoqg/b8hdPskxZXsJ
App.factory('MyObjectWithParam', ['$injector', function ($injector) { return function(name) { return $injector.instantiate(MyObjectWithParam,{ name: name }); }; }]);
在此处阅读更多信息:docs.angularjs.org/tutorial/step_05
.service
,为什么要这样做?
.factory
而不是 .service
有什么好处(percieved?)?
new Car('BMW')
和 new Car('Ford')
,它们不会共享相同的变量和所有内容。
以下是主要区别:
服务
语法:module.service( 'serviceName', function );
结果:将 serviceName 声明为可注入参数时,您将获得传递给 module.service
的函数实例。
用法:对于共享实用函数很有用,只需将 () 附加到注入的函数引用即可调用。也可以使用 injectedArg.call( this )
或类似方法运行。
工厂
语法:module.factory( 'factoryName', function );
结果:将 factoryName 声明为可注入参数时,您将获得通过调用传递给 module.factory
的函数引用返回的值。
用法:对于返回一个“类”函数可能很有用,然后可以新建该函数以创建实例。
还要检查 AngularJS documentation 和 stackoverflow confused about service vs factory 上的类似问题。
这是example using services and factory。阅读有关 AngularJS service vs factory 的更多信息。
除了第一个答案之外,我认为 .service() 适用于以更面向对象的风格(C#/Java)编写代码的人(使用 this 关键字并通过原型/构造函数实例化对象)。
Factory 适用于编写更自然的 javascript/函数式编码的代码的开发人员。
看一下 angular.js 中 .service 和 .factory 方法的源代码——在内部它们都调用了 provider 方法:
function provider(name, provider_) {
if (isFunction(provider_)) {
provider_ = providerInjector.instantiate(provider_);
}
if (!provider_.$get) {
throw Error('Provider ' + name + ' must define $get factory method.');
}
return providerCache[name + providerSuffix] = provider_;
}
function factory(name, factoryFn) { \
return provider(name, { $get: factoryFn });
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
很简单:
.service - 注册函数将作为构造函数调用(又名“newed”)
.factory - 注册函数将作为简单函数调用
两者都被调用一次,导致一个单例对象被注入到应用程序的其他组件中。
所有提供商的工作方式都相同。不同的方法 service
、factory
、provider
只是让您用更少的代码完成同样的事情。
PS 还有 value
和 constant
。
从 provider
开始到 value
结束的每个特殊情况都有一个额外的限制。所以要在它们之间做出决定,你必须问自己,哪一个可以让你用更少的代码完成你想要的。
这是一张图片,向您展示了我的意思:
https://i.stack.imgur.com/CkdHl.png
您可以在我从以下位置获得这张图片的博客文章中获得细分和参考指南:
console.log()
并注入多个控制器来确认这一点。
这里有更多关于服务与工厂的示例,它们可能有助于了解它们之间的区别。基本上,一个服务调用了“new ...”,它已经被实例化了。工厂不会自动实例化。
基本示例
返回具有单个方法的类对象
这是一个具有单一方法的服务:
angular.service('Hello', function () {
this.sayHello = function () { /* ... */ };
});
这是一个返回带有方法的对象的工厂:
angular.factory('ClassFactory', function () {
return {
sayHello: function () { /* ... */ }
};
});
返回一个值
返回数字列表的工厂:
angular.factory('NumberListFactory', function () {
return [1, 2, 3, 4, 5];
});
console.log(NumberListFactory);
返回数字列表的服务:
angular.service('NumberLister', function () {
this.numbers = [1, 2, 3, 4, 5];
});
console.log(NumberLister.numbers);
两种情况下的输出都是相同的,即数字列表。
高级示例
使用工厂的“类”变量
在这个例子中,我们定义了一个 CounterFactory,它增加或减少一个计数器,您可以获取当前计数或获取已创建的 CounterFactory 对象的数量:
angular.factory('CounterFactory', function () {
var number_of_counter_factories = 0; // class variable
return function () {
var count = 0; // instance variable
number_of_counter_factories += 1; // increment the class variable
// this method accesses the class variable
this.getNumberOfCounterFactories = function () {
return number_of_counter_factories;
};
this.inc = function () {
count += 1;
};
this.dec = function () {
count -= 1;
};
this.getCount = function () {
return count;
};
}
})
我们使用 CounterFactory
创建多个计数器。我们可以访问类变量来查看创建了多少计数器:
var people_counter;
var places_counter;
people_counter = new CounterFactory();
console.log('people', people_counter.getCount());
people_counter.inc();
console.log('people', people_counter.getCount());
console.log('counters', people_counter.getNumberOfCounterFactories());
places_counter = new CounterFactory();
console.log('places', places_counter.getCount());
console.log('counters', people_counter.getNumberOfCounterFactories());
console.log('counters', places_counter.getNumberOfCounterFactories());
这段代码的输出是:
people 0
people 1
counters 1
places 0
counters 2
counters 2
“工厂”和“服务”是 Angular 进行 DI(依赖注入)的不同方式。
因此,当我们使用“服务”定义 DI 时,如下面的代码所示。这将创建“Logger”对象的新 GLOBAL 实例并将其注入到函数中。
app.service("Logger", Logger); // Injects a global object
当您使用“工厂”定义 DI 时,它不会创建实例。它只是传递方法,之后消费者必须在内部调用工厂以获取对象实例。
app.factory("Customerfactory", CreateCustomer);
下面是一个简单的图像,它直观地显示了“服务”的 DI 流程与“工厂”的不同之处。
https://i.stack.imgur.com/M55Yi.png
当我们想根据场景创建不同类型的对象时,应该使用工厂。例如,根据场景,我们想要创建一个简单的“客户”对象,或“客户”与“地址”对象或“客户”与“电话”对象。 Here is a detailed explanation of this paragraph
当我们要注入实用程序或共享功能时,应该使用服务,例如 Utility , Logger , Error handler 等。
服务风格:(可能是最简单的一种)返回实际函数:对于共享实用函数很有用,这些实用函数只需将 () 附加到注入的函数引用即可调用。
AngularJS 中的服务是一个包含一组函数的单例 JavaScript 对象
var myModule = angular.module("myModule", []);
myModule.value ("myValue" , "12345");
function MyService(myValue) {
this.doIt = function() {
console.log("done: " + myValue;
}
}
myModule.service("myService", MyService);
myModule.controller("MyController", function($scope, myService) {
myService.doIt();
});
工厂风格:(更复杂但更复杂)返回函数的返回值:在 java 中实例化一个像 new Object() 这样的对象。
工厂是一个创造价值的功能。当服务、控制器等需要从工厂注入的值时,工厂会按需创建该值。一旦创建,该值就会被所有需要注入的服务、控制器等重用。
var myModule = angular.module("myModule", []);
myModule.value("numberValue", 999);
myModule.factory("myFactory", function(numberValue) {
return "a value: " + numberValue;
})
myModule.controller("MyController", function($scope, myFactory) {
console.log(myFactory);
});
提供者风格:(完整的、可配置的版本)返回函数的 $get 函数的输出:可配置。
AngularJS 中的提供者是您可以创建的最灵活的工厂形式。您可以像使用服务或工厂一样向模块注册提供程序,但您使用 provider() 函数代替。
var myModule = angular.module("myModule", []);
myModule.provider("mySecondService", function() {
var provider = {};
var config = { configParam : "default" };
provider.doConfig = function(configParam) {
config.configParam = configParam;
}
provider.$get = function() {
var service = {};
service.doService = function() {
console.log("mySecondService: " + config.configParam);
}
return service;
}
return provider;
});
myModule.config( function( mySecondServiceProvider ) {
mySecondServiceProvider.doConfig("new config param");
});
myModule.controller("MyController", function($scope, mySecondService) {
$scope.whenButtonClicked = function() {
mySecondService.doIt();
}
});
基本区别在于,提供者允许将原始(非对象)、数组或回调函数值设置到工厂声明的变量中,因此如果返回一个对象,则必须显式声明并返回它。
另一方面,服务只能用于将服务声明的变量设置为对象,因此我们可以避免对象的显式创建和返回,而另一方面它允许使用 this 关键字。
或者简而言之,“提供者是一种更通用的形式,而服务仅限于对象”。
这就是我在设计模式方面理解它们之间的区别的方式:
服务:返回一个类型,它将被更新以创建该类型的对象。如果使用 Java 类比,Service 返回一个 Java 类定义。
Factory:返回一个可以立即使用的具体对象。在 Java 类比中,工厂返回一个 Java 对象。
经常让人们(包括我自己)感到困惑的部分是,当您在代码中注入服务或工厂时,它们可以以相同的方式使用,在这两种情况下,您在代码中得到的是一个可以立即调用的具体对象。这意味着在服务的情况下,角度代表您在服务声明中调用“新”。我认为这是一个复杂的概念。
这将是理解 Service Vs Factory Vs Provider 的最佳和简短答案
来源:https://groups.google.com/forum/#!msg/angular/56sdORWEoqg/HuZsOsMvKv4J
以下是 ben 用 demo 说的http://jsbin.com/ohamub/1/edit?html,output
“代码中有一些注释说明了主要区别,但我会在这里对它们进行一些扩展。作为说明,我只是对此有所了解,所以如果我说任何错误的地方,请告诉我。
服务
语法:module.service('serviceName', function);
结果:将 serviceName 声明为可注入参数时,将为您提供传递给 module.service 的实际函数引用。
用法:对于共享实用函数很有用,只需将 () 附加到注入的函数引用即可调用。也可以使用 injectArg.call( this ) 或类似方法运行。
工厂
语法:module.factory('factoryName', function);
结果:当将 factoryName 声明为可注入参数时,您将获得通过调用传递给 module.factory 的函数引用返回的值。
用法:对于返回一个“类”函数可能很有用,然后可以新建该函数以创建实例。
提供者
语法:module.provider('providerName', function);
结果:当将 providerName 声明为可注入参数时,您将获得通过调用传递给 module.provider 的函数引用的 $get 方法返回的值。
用法:对于返回一个“类”函数可能很有用,然后可以新建该函数以创建实例,但这需要在注入之前进行某种配置。也许对跨项目可重用的类有用?这个还是有点朦胧。”本
我有一段时间感到困惑,我正在尽力在这里提供一个简单的解释。希望这会有所帮助!
angular .factory
和 angular .service
都用于初始化服务并以相同的方式工作。
唯一的区别是,您希望如何初始化您的服务。
两者都是单身人士
var app = angular.module('app', []);
工厂
app.factory(
如果您想从具有返回值的函数初始化您的服务,则必须使用此 factory
方法。
例如
function myService() {
//return what you want
var service = {
myfunc: function (param) { /* do stuff */ }
}
return service;
}
app.factory('myService', myService);
注入此服务时(例如,注入您的控制器):
Angular 将调用你给定的函数(作为 myService())来返回对象
Singleton - 只调用一次,存储和传递同一个对象。
服务
app.service(<服务名称>, <构造函数>)
如果您想从构造函数(使用 this
关键字)初始化您的服务,则必须使用此 service
方法。
例如
function myService() {
this.myfunc: function (param) { /* do stuff */ }
}
app.service('myService', myService);
注入此服务时(例如,注入您的控制器):
Angular 将更新您的给定函数(作为 new myService())以返回对象
Singleton - 只调用一次,存储和传递同一个对象。
工厂
<构造函数>
服务
<有返回值的函数>
示例 - 演示
Angular 服务与工厂
Angular 服务与工厂(带路由)
多亏了 Pascal Precht 的一篇博文,这帮助我理解了其中的区别。
服务是模块上的一种方法,它采用名称和定义服务的函数。您可以在其他组件(如控制器、指令和过滤器)中注入和使用该特定服务。工厂是模块上的一种方法,它还带有一个名称和一个函数,用于定义工厂。我们也可以像处理服务一样注入和使用它。
使用 new 创建的对象使用其构造函数的原型属性的值作为其原型,因此我找到了调用 Object.create() 的 Angular 代码,我相信它是实例化时的服务构造函数。然而,工厂函数实际上只是一个被调用的函数,这就是为什么我们必须为工厂返回一个对象字面量。
这是我为工厂找到的 Angular 1.5 代码:
var needsRecurse = false;
var destination = copyType(source);
if (destination === undefined) {
destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
needsRecurse = true;
}
factory() 函数的 Angular 源代码片段:
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
}
它接受名称和传递的工厂函数,并返回一个具有相同名称的提供者,它有一个 $get 方法,这是我们的工厂函数。每当您向注入器询问特定依赖项时,它基本上都会通过调用 $get() 方法向相应的提供者询问该服务的实例。这就是创建提供程序时需要 $get() 的原因。
这是用于服务的 Angular 1.5 代码。
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
原来我们调用service()的时候,其实调用的是factory()!但是,它不只是将我们的服务构造函数按原样传递给工厂。它还传递了一个函数,该函数要求注入器通过给定的构造函数实例化一个对象。
换句话说,如果我们在某处注入 MyService,代码中会发生什么:
MyServiceProvider.$get(); // return the instance of the service
再次重申它,服务调用工厂,这是相应提供程序上的 $get() 方法。此外,$injector.instantiate() 是最终使用构造函数调用 Object.create() 的方法。这就是我们在服务中使用“this”的原因。
对于 ES5,我们使用哪个无关紧要:service() 或 factory(),它始终是一个被调用的工厂,它为我们的服务创建一个提供者。
你也可以对服务做同样的事情。服务是一个构造函数,但是,它不会阻止我们返回对象字面量。所以我们可以把我们的服务代码写成它基本上和我们的工厂做同样的事情,或者换句话说,你可以把服务写成工厂来返回一个对象。
为什么大多数人建议使用工厂而不是服务?这是我从 Pawel Kozlowski 的书:Mastering Web Application Development with AngularJS 中看到的最佳答案。
工厂方法是将对象放入 AngularJS 依赖注入系统的最常用方法。它非常灵活,可以包含复杂的创建逻辑。由于工厂是常规函数,我们还可以利用新的词法作用域来模拟“私有”变量。这非常有用,因为我们可以隐藏给定服务的实现细节。”
使用工厂,您实际上在工厂内部创建了一个对象并将其返回。
使用该服务,您只需一个使用 this 关键字定义函数的标准函数。
使用提供程序,您可以定义一个 $get ,它可用于获取返回数据的对象。
在 AngularJS 中有三种处理业务逻辑的方法:(受 Yaakov 的 Coursera AngularJS 课程启发)它们是:
服务工厂供应商
这里我们只讨论Service vs Factory
服务:
句法:
应用程序.js
var app = angular.module('ServiceExample',[]);
var serviceExampleController =
app.controller('ServiceExampleController', ServiceExampleController);
var serviceExample = app.service('NameOfTheService', NameOfTheService);
ServiceExampleController.$inject = ['NameOfTheService'] //very important as this protects from minification of js files
function ServiceExampleController(NameOfTheService){
serviceExampleController = this;
serviceExampleController.data = NameOfTheService.getSomeData();
}
function NameOfTheService(){
nameOfTheService = this;
nameOfTheService.data = "Some Data";
nameOfTheService.getSomeData = function(){
return nameOfTheService.data;
}
}
索引.html
<div ng-controller = "ServiceExampleController as serviceExample">
{{serviceExample.data}}
</div>
服务的主要特点:
惰性实例化:如果服务没有被注入,它将永远不会被实例化。因此,要使用它,您必须将其注入到模块中。单例:如果它被注入到多个模块中,那么所有模块都只能访问一个特定的实例。这就是为什么在不同的控制器之间共享数据非常方便。
工厂
现在让我们谈谈AngularJS中的工厂
首先让我们看一下语法:
应用程序.js:
var app = angular.module('FactoryExample',[]);
var factoryController = app.controller('FactoryController', FactoryController);
var factoryExampleOne = app.factory('NameOfTheFactoryOne', NameOfTheFactoryOne);
var factoryExampleTwo = app.factory('NameOfTheFactoryTwo', NameOfTheFactoryTwo);
//first implementation where it returns a function
function NameOfTheFactoryOne(){
var factory = function(){
return new SomeService();
}
return factory;
}
//second implementation where an object literal would be returned
function NameOfTheFactoryTwo(){
var factory = {
getSomeService : function(){
return new SomeService();
}
};
return factory;
}
现在在控制器中使用上述两个:
var factoryOne = NameOfTheFactoryOne() //since it returns a function
factoryOne.someMethod();
var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object
factoryTwo.someMethod();
工厂特点:
这种类型的服务遵循工厂设计模式。工厂可以被认为是创建新对象或方法的中心位置。这不仅会产生单例,还会产生可定制的服务。 .service() 方法是一个工厂,它总是产生相同类型的服务,即单例。没有简单的方法来配置它的行为。该 .service() 方法通常用作不需要任何配置的快捷方式。
有关简短的说明,请参阅 https://stackoverflow.com/a/26924234/5811973。
有关详细说明,请参阅 https://stackoverflow.com/a/15666049/5811973。
https://i.stack.imgur.com/24jgv.png
您可以通过这个类比来理解差异 - 考虑将返回一些值的普通函数和将使用 new 关键字实例化的构造函数之间的区别。因此创建工厂类似于创建将返回一些值的普通函数(原始或一个对象),而创建服务就像创建构造函数(OO 类),我们可以使用 new 关键字创建实例。唯一需要注意的是,当我们使用 Service 方法创建服务时,它会使用 AngularJS 支持的依赖注入机制自动创建它的实例
new
。