AngularJS中的服务、提供商和工厂之间有什么区别?
当前回答
所有的好答案都已经有了。我想在服务和工厂方面再补充几点。以及服务/工厂之间的差异。你也可以有这样的问题:
我应该使用服务还是工厂?有什么不同?他们的行为是否相同?
让我们从服务和工厂之间的区别开始:
两者都是Singleton:每当Angular第一次发现它们作为依赖项时,它都会创建服务/工厂的单个实例。创建实例后,将永远使用同一实例。可用于对具有行为的对象进行建模:它们都可以有方法、内部状态变量等。尽管编写代码的方式会有所不同。
服务:
服务是一个构造函数,Angular将通过调用newyourServiceName()来实例化它。这意味着一些事情。
函数和实例变量将是它的财产。您不需要返回值。当Angular调用new yourServiceName()时,它将接收此对象以及您在其上放置的所有财产。
示例:
angular.service('MyService', function() {
this.aServiceVariable = "Ved Prakash"
this.aServiceMethod = function() {
return //code
};
});
当Angular将此MyService服务注入控制器时取决于它,该控制器将获得它可以调用的MyService上的函数,例如MyService.aServiceMethod()。
注意:
由于构造的服务是一个对象,因此当调用它们时,它内部的方法可以引用它:
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++;
};
});
例如,如果您通过从服务器$http.get('/score').then(ScoreKeeper.setScore)获取分数来初始化分数,那么您可能会尝试在承诺链中调用ScoreKeeper。这样做的问题是,ScoreKeeer.setScore将在该绑定为空的情况下被调用,并且您会收到错误。更好的方法是$http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper))。无论您是否选择在服务方法中使用它,请注意如何调用它们。
从服务返回值:
由于JavaScript构造函数的工作方式,若从构造函数函数返回一个复杂值(即Object),调用者将获得该Object而不是该实例。
这意味着你基本上可以从下面复制粘贴工厂示例,用服务替换工厂,它会起作用:
angular.service('MyService', function($http) {
var api = {};
api.aServiceMethod= function() {
return $http.get('/users');
};
return api;
});
因此,当Angular使用新的MyService()构造服务时,它将获得该api对象而不是MyService实例。
这是任何复杂值(对象、函数)的行为,但不是基本类型的行为。
工厂:
工厂是一个返回值的普通旧函数。返回值是注入到依赖于工厂的事物中的值。Angular中的一个典型工厂模式是返回一个具有函数作为财产的对象,如下所示:
angular.factory('MyFactory', function($http) {
var api = {};
api.aFactoryMethod= function() {
return $http.get('/users');
};
return api;
});
工厂依赖项的注入值是工厂的返回值值,并且它不必是对象。这可能是一种功能
以上1和2个问题的答案:
大多数情况下,只要坚持使用工厂就可以了。他们的行为更容易理解。别无选择关于是否返回一个值,此外,没有错误如果你做错事,就会被介绍。当我谈论注射时,我仍然将其称为“服务”但它们是依赖关系。服务/工厂行为非常相似,有些人会说任何一个都很好。这有点正确,但我觉得遵循约翰·帕帕风格指南的建议,坚持工厂**
其他回答
已经有好的答案了,但我只想分享这个答案。
首先:Provider是创建服务(单例对象)的方法/方法,该服务假设由$injector注入(AngulaJS如何处理IoC模式)。
以及价值、工厂、服务和常量(4种方式)——相对于提供者方式/接收器的语法糖。
服务与工厂部分已涵盖:https://www.youtube.com/watch?v=BLzNCkPn3ao
服务都是关于新关键字的,实际上我们知道它有四个方面:
创建全新对象将其链接到其原型对象将上下文连接到此并返回此
工厂就是工厂模式的全部内容——包含返回类似服务的对象的函数。
使用其他服务的能力(具有依赖关系)服务初始化延迟/延迟初始化
这个简单/简短的视频:还包括提供商:https://www.youtube.com/watch?v=HvTZbQ_hUZY(在那里你可以看到他们是如何从工厂走向供应商的)
在应用程序完全启动/初始化之前,主要在应用程序配置中使用提供程序配方。
从AngularJS邮件列表中,我得到了一个令人惊叹的线程,它解释了服务与工厂与供应商以及它们的注入用法。编写答案:
服务
语法:module.service('serviceName',function);结果:当将serviceName声明为可注入参数时,将为您提供函数的实例。换句话说,新的FunctionYouPassedToService()。
工厂
语法:module.factory('factoryName',function);结果:当将factoryName声明为可注入参数时,将提供通过调用传递给module.factory的函数引用返回的值。
提供商
语法:module.provider('providerName',function);结果:当将providerName声明为可注入参数时,将提供(newProviderFunction())$get()。在调用$get方法之前实例化构造函数函数-ProviderFunction是传递给module.provider的函数引用。
提供程序的优点是可以在模块配置阶段进行配置。
请参阅此处获取提供的代码。
下面是Misko的进一步解释:
provide.value('a', 123);
function Controller(a) {
expect(a).toEqual(123);
}
在这种情况下,注入器只是按原样返回值。但是如果您想计算该值呢?然后使用工厂
provide.factory('b', function(a) {
return a*2;
});
function Controller(b) {
expect(b).toEqual(246);
}
因此,工厂是一个负责创造价值的职能部门。注意,工厂函数可以要求其他依赖项。
但是,如果你想成为更多的OO,并拥有一个名为Greeter的类呢?
function Greeter(a) {
this.greet = function() {
return 'Hello ' + a;
}
}
然后,要实例化,必须编写
provide.factory('greeter', function(a) {
return new Greeter(a);
});
然后我们可以像这样在控制器中请求“问候者”
function Controller(greeter) {
expect(greeter instanceof Greeter).toBe(true);
expect(greeter.greet()).toEqual('Hello 123');
}
但这太啰嗦了。一个简短的写法是provider.service(“greeter”,greeter);
但是如果我们想在注入之前配置Greeter类呢?然后我们可以写
provide.provider('greeter2', function() {
var salutation = 'Hello';
this.setSalutation = function(s) {
salutation = s;
}
function Greeter(a) {
this.greet = function() {
return salutation + ' ' + a;
}
}
this.$get = function(a) {
return new Greeter(a);
};
});
然后我们可以这样做:
angular.module('abc', []).config(function(greeter2Provider) {
greeter2Provider.setSalutation('Halo');
});
function Controller(greeter2) {
expect(greeter2.greet()).toEqual('Halo 123');
}
顺便说一句,服务、工厂和价值都来自提供者。
provider.service = function(name, Class) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.instantiate(Class);
};
});
}
provider.factory = function(name, factory) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.invoke(factory);
};
});
}
provider.value = function(name, value) {
provider.factory(name, function() {
return value;
});
};
我注意到了一些有趣的事情,当我和提供者一起玩的时候。
与服务和工厂相比,供应商对注射剂的可见性有所不同。如果您声明AngularJS“常量”(例如,myApp.constant('a','Robert');),您可以将其注入服务、工厂和提供商。
但是如果您声明一个AngularJS“value”(例如,myApp.value('b',{name:'Jones‘});),您可以将其注入服务和工厂,但不能注入提供程序创建函数。但是,您可以将其注入为提供者定义的$get函数中。AngularJS文档中提到了这一点,但很容易错过。您可以在%provide页面的value和constant方法部分找到它。
http://jsfiddle.net/R2Frv/1/
<div ng-app="MyAppName">
<div ng-controller="MyCtrl">
<p>from Service: {{servGreet}}</p>
<p>from Provider: {{provGreet}}</p>
</div>
</div>
<script>
var myApp = angular.module('MyAppName', []);
myApp.constant('a', 'Robert');
myApp.value('b', {name: 'Jones'});
myApp.service('greetService', function(a,b) {
this.greeter = 'Hi there, ' + a + ' ' + b.name;
});
myApp.provider('greetProvider', function(a) {
this.firstName = a;
this.$get = function(b) {
this.lastName = b.name;
this.fullName = this.firstName + ' ' + this.lastName;
return this;
};
});
function MyCtrl($scope, greetService, greetProvider) {
$scope.servGreet = greetService.greeter;
$scope.provGreet = greetProvider.fullName;
}
</script>
此答案针对主题/问题
Factory、Service和Constant是如何在提供者配方之上添加语法糖的?
OR
工厂、服务和供应商在内部是怎样的
基本上发生的是
当您创建factory()时,它会将第二个参数中提供的函数设置为provider的$get并返回它(provider(name,{$get:factoryFn})),您所得到的只是provider,但除了$get之外没有其他属性/方法(这意味着您无法配置)
工厂源代码
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
};
当生成服务()时,它返回您提供factory()的函数,该函数注入构造函数(返回您在服务中提供的构造函数的实例)并返回它
服务源代码
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
};
因此,基本上在这两种情况下,您最终都会将providers$get设置为您提供的函数,但您可以提供$get以外的任何内容,因为您最初可以在provider()中为config块提供
下面是我为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>
这里有一个更简单的例子。我使用的是一些第三方库,它们期望“Position”对象显示纬度和经度,但通过不同的对象财产。我不想破解供应商代码,所以我调整了我传递的“位置”对象。
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
;
推荐文章
- 在AngularJS中集成jQuery插件的正确方法
- 如何设置一个iframe src属性从一个变量在AngularJS
- 如何在Angular中使用$rootScope来存储变量?
- AngularJS:工厂和服务?
- 父ng-repeat从子ng-repeat的访问索引
- 无法解析类型为“Microsoft.AspNetCore.Http.IHttpContextAccessor”的服务
- AngularJS使用ng-class切换类
- 如何在Angular.js中配置不同的环境?
- 当使用ng-model时,输入文本框上的Value属性被忽略?
- 使用AngularJS跟踪谷歌分析页面视图
- 传递参数
- 在“DOMWindow”上执行“postMessage”失败:https://www.youtube.com !== http://localhost:9000
- Angular中相当于AngularJS $watch的东西是什么?
- AngularJS路由不带散列“#”
- 单击表单中的按钮会刷新页面