多亏了 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 依赖注入系统的最常用方法。它非常灵活,可以包含复杂的创建逻辑。由于工厂是常规函数,我们还可以利用新的词法作用域来模拟“私有”变量。这非常有用,因为我们可以隐藏给定服务的实现细节。”