多亏了Pascal Precht的一篇博客文章,这帮助我理解了其中的区别。
服务是模块上的一个方法,它接受定义服务的名称和函数。你可以在其他组件中注入和使用特定的服务,比如控制器、指令和过滤器。工厂是模块上的一个方法,它还接受一个定义工厂的名称和函数。我们还可以像对服务那样注入和使用it。
用new创建的对象使用其构造函数的prototype属性的值作为原型,所以我找到了调用Object.create()的Angular代码,我相信它是服务构造函数的实例化。然而,工厂函数实际上只是一个被调用的函数,这就是为什么我们必须为工厂返回一个对象字面量。
下面是我为factory找到的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依赖注入系统。它是非常灵活的,可以
包含复杂的创建逻辑。因为工厂是正规的
函数,也可以利用新的词法作用域来实现
模拟“私有”变量。这很有用,因为我们可以隐藏
给定服务的实现细节。”