AngularJS中的服务、提供商和工厂之间有什么区别?
当前回答
为了澄清这一点,从AngularJS源代码中,您可以看到一个服务只是调用工厂函数,而工厂函数反过来调用提供程序函数:
function factory(name, factoryFn) {
return provider(name, { $get: factoryFn });
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
其他回答
我对此事的澄清:
基本上,所有提到的类型(服务、工厂、提供者等)都只是创建和配置全局变量(当然,这些变量对整个应用程序是全局的),就像老式的全局变量一样。
虽然不建议使用全局变量,但这些全局变量的实际用途是通过将变量传递给相关控制器来提供依赖注入。
在为“全局变量”创建值时有许多复杂程度:
常数这定义了一个在整个应用程序中不应修改的实际常量,就像其他语言中的常量一样(JavaScript缺少的)。价值这是一个可修改的值或对象,它充当一些全局变量,甚至可以在创建其他服务或工厂时注入(请参阅有关这些的详细信息)。然而,它必须是一个“文字值”,这意味着必须写出实际值,并且不能使用任何计算或编程逻辑(换句话说,39或myText或{prop:“value”}可以,但2+2不行)。工厂一个更一般的值,可以立即计算。它的工作原理是将一个函数传递给AngularJS,其中包含计算值所需的逻辑,AngularJS执行该函数,并将返回值保存在命名变量中。请注意,可以返回对象(在这种情况下,它的功能类似于服务)或函数(将作为回调函数保存在变量中)。服务服务是工厂的更精简版本,只有当值是对象时才有效,它允许直接在函数中写入任何逻辑(就像它是构造函数一样),以及使用this关键字声明和访问对象财产。供应商与工厂的简化版本的服务不同,提供程序是一种更复杂但更灵活的初始化“全局”变量的方法,最大的灵活性是从app.config中设置值的选项。它的工作方式类似于使用服务和提供者的组合,方法是向提供者传递一个函数,该函数具有使用this关键字声明的财产,该关键字可以从app.config中使用。然后它需要有一个单独的$.get函数,该函数由AngularJS在通过app.config文件设置上述财产后执行,该$.get功能的行为与上述工厂相同,因为它的返回值用于初始化“全局”变量。
工厂:您实际在工厂内部创建对象并将其返回的工厂。service:您只有一个使用this关键字定义函数的标准函数的服务。provider:您定义了一个$get,它可以用来获取返回数据的对象。
此答案针对主题/问题
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
;
JS Fiddle演示
工厂/服务/供应商的“Hello world”示例:
var myApp=角度模块('myApp',[]);//服务风格,可能是最简单的myApp.service('helloWorldFromService',函数(){this.sayHello=函数(){return“你好,世界!”;};});//工厂风格,更加复杂myApp.factory('helloWorldFromFactory',函数(){返回{sayHello:函数(){return“你好,世界!”;}};});//提供程序风格、全面、可配置版本myApp.provider('helloWorld',函数(){this.name='默认值';这一点$get=函数(){var名称=this.name;返回{sayHello:函数(){return“您好,”+name+“!”;}}};this.setName=函数(名称){this.name=名称;};});//嘿,我们可以配置提供者!myApp.config(函数(helloWorldProvider){helloWorldProvider.setName('World');});函数MyCtrl($scope、helloWorld、helloWorldFromFactory、helloWorldFromService){$scope.hellos=[helloWorld.sayHello(),helloWorldFromFactory.sayHello(),helloWorldFromService.sayHello()];}<script src=“https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js“></script><body ng app=“myApp”><div ng controller=“MyCtrl”>{{hellos}}</div></body>
推荐文章
- AngularJS控制器的生命周期是什么?
- $destroy是否删除事件监听器?
- 用布尔值将单选按钮绑定到模型
- AngularJS只适用于单页应用程序吗?
- angular.js中的内联条件
- 如何突出显示当前菜单项?
- 如何使用AngularJS获取url参数
- angularjs中的compile函数和link函数有什么区别
- 如何向一个5岁的孩子解释依赖注入?
- Ng-repeat结束事件
- 缓存一个HTTP 'Get'服务响应在AngularJS?
- 从ng-click获取原始元素
- Angular JS:当我们已经有了具有作用域的指令控制器时,指令的link函数还需要什么?
- Angularjs的ng-model不能在ng-if中工作
- AngularJS禁用了开发机器上的部分缓存