AngularJS中的服务、提供商和工厂之间有什么区别?
当前回答
下面是我为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
;
其他回答
对我来说,理解差异的最佳和最简单的方法是:
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
}
}
最简单的思考方式如下:
服务是一个单例对象实例。如果您想为代码提供单例对象,请使用服务。工厂是一个阶级。如果您想为代码提供自定义类,请使用工厂(无法使用服务完成,因为它们已经实例化)。
工厂“类”示例在周围的注释中提供,以及提供程序差异。
了解AngularJS工厂、服务和提供商
所有这些都用于共享可重用的单例对象。它有助于在应用程序/各种组件/模块之间共享可重用代码。
来自文档服务/工厂:延迟实例化–Angular仅在应用程序组件依赖于服务/工厂时实例化它。单件–每个组件依赖于服务获取对单个实例的引用由服务工厂生成。
工厂
工厂是一个函数,您可以在创建对象之前操作/添加逻辑,然后返回新创建的对象。
app.factory('MyFactory', function() {
var serviceObj = {};
//creating an object with methods/functions or variables
serviceObj.myFunction = function() {
//TO DO:
};
//return that object
return serviceObj;
});
用法
它可以只是像类一样的函数集合。因此,当您将其注入控制器/工厂/指令函数时,它可以在不同的控制器中实例化。每个应用程序只实例化一次。
服务
在查看服务时,只需考虑阵列原型。服务是一个使用“new”关键字实例化新对象的函数。您可以使用this关键字将财产和函数添加到服务对象。与工厂不同,它不返回任何内容(它返回包含方法/财产的对象)。
app.service('MyService', function() {
//directly binding events to this context
this.myServiceFunction = function() {
//TO DO:
};
});
用法
当您需要在整个应用程序中共享单个对象时,请使用它。例如,经过验证的用户详细信息、可共享的方法/数据、实用程序功能等。
供应商
提供程序用于创建可配置的服务对象。您可以通过config函数配置服务设置。它使用$get()函数返回一个值。$get函数在运行阶段以角度执行。
app.provider('configurableService', function() {
var name = '';
//this method can be be available at configuration time inside app.config.
this.setName = function(newName) {
name = newName;
};
this.$get = function() {
var getName = function() {
return name;
};
return {
getName: getName //exposed object to where it gets injected.
};
};
});
用法
当您需要在服务对象可用之前为其提供模块化配置时,例如,假设您希望根据环境(如dev、stage或prod)设置API URL
注释只有提供程序在angular的配置阶段可用,而服务和工厂不是。
希望这能澄清您对工厂、服务和供应商的理解。
从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;
});
};
对我来说,当我意识到它们都以相同的方式工作时,我就发现了这一点:通过运行一次某个东西,存储它们获得的值,然后在通过依赖注入引用时提取相同的存储值。
假设我们有:
app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);
三者之间的区别在于:
a的存储值来自运行fn。b的存储值来自newing fn。c的存储值来自首先通过newing fn获取一个实例,然后运行该实例的$get方法。
这意味着在AngularJS中有一个类似于缓存对象的东西,每次注入的值仅在第一次注入时分配一次,其中:
cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()
这就是为什么我们在服务中使用这个,并定义一个This$获取提供者。
已经有好的答案了,但我只想分享这个答案。
首先:Provider是创建服务(单例对象)的方法/方法,该服务假设由$injector注入(AngulaJS如何处理IoC模式)。
以及价值、工厂、服务和常量(4种方式)——相对于提供者方式/接收器的语法糖。
服务与工厂部分已涵盖:https://www.youtube.com/watch?v=BLzNCkPn3ao
服务都是关于新关键字的,实际上我们知道它有四个方面:
创建全新对象将其链接到其原型对象将上下文连接到此并返回此
工厂就是工厂模式的全部内容——包含返回类似服务的对象的函数。
使用其他服务的能力(具有依赖关系)服务初始化延迟/延迟初始化
这个简单/简短的视频:还包括提供商:https://www.youtube.com/watch?v=HvTZbQ_hUZY(在那里你可以看到他们是如何从工厂走向供应商的)
在应用程序完全启动/初始化之前,主要在应用程序配置中使用提供程序配方。
推荐文章
- 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禁用了开发机器上的部分缓存