我已经看到angular.factory()和angular.service()被用来声明服务;然而,我找不到角。在官方文件的任何地方提供服务。
这两种方法有什么区别? 哪个应该用来做什么(假设它们做不同的事情)?
我已经看到angular.factory()和angular.service()被用来声明服务;然而,我找不到角。在官方文件的任何地方提供服务。
这两种方法有什么区别? 哪个应该用来做什么(假设它们做不同的事情)?
当前回答
工厂模式更灵活,因为它可以返回函数和值以及对象。
恕我直言,在服务模式中没有太多意义,因为它所做的一切都可以通过工厂轻松完成。例外可能是:
如果您出于某种原因关心实例化服务的声明类型——如果您使用服务模式,则构造函数将是新服务的类型。 如果您已经有一个正在其他地方使用的构造函数,并且还想将其用作服务(尽管如果您想向其中注入任何东西,可能没有多大用处!)。
Arguably, the service pattern is a slightly nicer way to create a new object from a syntax point of view, but it's also more costly to instantiate. Others have indicated that angular uses "new" to create the service, but this isn't quite true - it isn't able to do that because every service constructor has a different number of parameters. What angular actually does is use the factory pattern internally to wrap your constructor function. Then it does some clever jiggery pokery to simulate javascript's "new" operator, invoking your constructor with a variable number of injectable arguments - but you can leave out this step if you just use the factory pattern directly, thus very slightly increasing the efficiency of your code.
其他回答
App.factory ('fn',fn) vs. app.service('fn',fn)
建设
对于工厂,Angular将调用该函数来获得结果。它是被缓存和注入的结果。
//factory
var obj = fn();
return obj;
对于服务,Angular会通过调用new来调用构造函数。构造的函数被缓存和注入。
//service
var obj = new fn();
return obj;
实现
工厂通常返回一个对象文字,因为返回值是注入到控制器、运行块、指令等中的值
app.factory('fn', function(){
var foo = 0;
var bar = 0;
function setFoo(val) {
foo = val;
}
function setBar (val){
bar = val;
}
return {
setFoo: setFoo,
serBar: setBar
}
});
服务函数通常不返回任何东西。相反,它们执行初始化和公开函数。函数也可以引用“this”,因为它是用“new”构造的。
app.service('fn', function () {
var foo = 0;
var bar = 0;
this.setFoo = function (val) {
foo = val;
}
this.setBar = function (val){
bar = val;
}
});
结论
当涉及到使用工厂或服务时,它们都非常相似。它们被注入到控制器、指令、运行块等中,并以几乎相同的方式用于客户端代码。它们都是单例的——这意味着同一个实例在注入服务/工厂的所有地方共享。
那么你更喜欢哪一种呢?不管是哪一种,它们都非常相似,差异微不足道。如果您选择其中一个而不是另一个,请注意它们是如何构造的,以便您可以正确地实现它们。
博士TL; 1)当你使用Factory时,你创建一个对象,向它添加属性,然后返回相同的对象。当你将这个工厂传递给你的控制器时,对象上的那些属性现在将通过你的工厂在那个控制器中可用。
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.artist = myFactory.getArtist();
});
app.factory('myFactory', function(){
var _artist = 'Shakira';
var service = {};
service.getArtist = function(){
return _artist;
}
return service;
});
2)当你在使用Service时,Angular会在幕后用' new '关键字实例化它。正因为如此,你将为' this '添加属性,服务将返回' this '。当你把服务传递给你的控制器时,“this”上的那些属性现在将通过你的服务在控制器上可用。
app.controller('myServiceCtrl', function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service('myService', function(){
var _artist = 'Nelly';
this.getArtist = function(){
return _artist;
}
});
不博士TL;
1)工厂 工厂是创建和配置服务的最流行的方式。实际上没有比TL和DR说的更多的东西了。你只需要创建一个对象,向其添加属性,然后返回相同的对象。然后当你把工厂传递给你的控制器时,对象上的那些属性现在将通过你的工厂在控制器中可用。下面是一个更广泛的示例。
app.factory('myFactory', function(){
var service = {};
return service;
});
现在,当我们将' myFactory '传递给控制器时,任何附加到' service '的属性都将可用。
现在让我们向回调函数中添加一些“私有”变量。这些不会直接从控制器访问,但我们最终将在' service '上设置一些getter/setter方法,以便在需要时能够更改这些'私有'变量。
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK';
return _finalUrl
}
return service;
});
在这里你会注意到我们没有将这些变量/函数附加到“service”。我们创建它们只是为了以后使用或修改它们。
baseUrl是iTunes API需要的基URL _artist是我们要查找的艺术家 _finalUrl是最终的完全构建的URL,我们将调用iTunes makeUrl是一个函数,它将创建并返回我们的 iTunes友好的URL。
现在我们的助手/私有变量和函数已经就绪,让我们向“service”对象添加一些属性。无论我们把什么放在' service '上,我们都能够直接在任何控制器中使用' myFactory '。
我们将创建setArtist和getArtist方法,简单地返回或设置艺术家。我们还将创建一个方法,用我们创建的URL调用iTunes API。这个方法将返回一个承诺,它将在数据从iTunes API返回后实现。如果你没有在Angular中使用承诺的经验,我强烈建议你深入研究一下。
下面的setArtist接受一个艺术家,并允许你设置艺术家。itunes首先调用makeUrl()来构建我们将用于$http请求的URL。然后它设置一个promise对象,用我们最终的url发出$http请求,然后因为$http返回了一个promise,所以我们能够在请求后调用.success或.error。然后我们用iTunes数据来解决我们的承诺,或者我们用“有一个错误”的消息来拒绝它。
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
service.setArtist = function(artist){
_artist = artist;
}
service.getArtist = function(){
return _artist;
}
service.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
return service;
});
现在我们的工厂建成了。我们现在能够将' myFactory '注入到任何控制器中,然后我们将能够调用我们附加到服务对象的方法(setArtist, getArtist和callItunes)。
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.data = {};
$scope.updateArtist = function(){
myFactory.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myFactory.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
In the controller above we’re injecting in the ‘myFactory’ service. We then set properties on our $scope object that are coming from data from ‘myFactory’. The only tricky code above is if you’ve never dealt with promises before. Because callItunes is returning a promise, we are able to use the .then() method and only set $scope.data.artistData once our promise is fulfilled with the iTunes data. You’ll notice our controller is very ‘thin’. All of our logic and persistent data is located in our service, not in our controller. 2) Service Perhaps the biggest thing to know when dealing with creating a Service is that that it’s instantiated with the ‘new’ keyword. For you JavaScript gurus this should give you a big hint into the nature of the code. For those of you with a limited background in JavaScript or for those who aren’t too familiar with what the ‘new’ keyword actually does, let’s review some JavaScript fundamentals that will eventually help us in understanding the nature of a Service.
为了真正看到使用' new '关键字调用函数时发生的变化,让我们创建一个函数并使用' new '关键字调用它,然后让我们展示解释器在看到' new '关键字时的操作。最终的结果是一样的。
首先,让我们创建构造函数。
var Person = function(name, age){
this.name = name;
this.age = age;
}
这是一个典型的JavaScript构造函数。现在,每当我们使用' new '关键字调用Person函数时,' this '将被绑定到新创建的对象。
现在让我们在Person的原型上添加一个方法,这样它就可以在Person '类'的每个实例上使用。
Person.prototype.sayName = function(){
alert('My name is ' + this.name);
}
现在,因为我们将sayName函数放在原型上,所以Person的每个实例都能够调用sayName函数,以便提醒该实例的名称。
现在我们有了Person构造函数和sayName函数的原型,让我们实际创建一个Person的实例,然后调用sayName函数。
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'
创建Person构造函数,向原型添加函数,创建Person实例,然后在原型上调用函数的所有代码都是这样的。
var Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
alert('My name is ' + this.name);
}
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'
Now let’s look at what actually is happening when you use the ‘new’ keyword in JavaScript. First thing you should notice is that after using ‘new’ in our example, we’re able to call a method (sayName) on ‘tyler’ just as if it were an object - that’s because it is. So first, we know that our Person constructor is returning an object, whether we can see that in the code or not. Second, we know that because our sayName function is located on the prototype and not directly on the Person instance, the object that the Person function is returning must be delegating to its prototype on failed lookups. In more simple terms, when we call tyler.sayName() the interpreter says “OK, I’m going to look on the ‘tyler’ object we just created, locate the sayName function, then call it. Wait a minute, I don’t see it here - all I see is name and age, let me check the prototype. Yup, looks like it’s on the prototype, let me call it.”.
下面的代码告诉你如何思考' new '关键字在JavaScript中的实际作用。它基本上是上述段落的一个代码示例。我把“解释器视图”或解释器在注释中查看代码的方式放在了注释中。
var Person = function(name, age){
//The line below this creates an obj object that will delegate to the person's prototype on failed lookups.
//var obj = Object.create(Person.prototype);
//The line directly below this sets 'this' to the newly created object
//this = obj;
this.name = name;
this.age = age;
//return this;
}
现在,了解了JavaScript中' new '关键字的实际作用后,在Angular中创建服务应该更容易理解了。
在创建Service时,最重要的是要了解Service是用' new '关键字实例化的。将上述知识与我们上面的例子结合起来,你现在应该认识到,你将直接将你的属性和方法附加到' this ',然后从服务本身返回。让我们来看看实际情况。
与我们最初在Factory示例中所做的不同,我们不需要创建一个对象然后返回该对象,因为就像之前多次提到的那样,我们使用了' new '关键字,因此解释器将创建该对象,让它委托给它的原型,然后为我们返回它,而不需要我们做这些工作。
首先,让我们创建“private”和helper函数。这看起来很熟悉,因为我们在我们的工厂做了完全相同的事情。我不会解释每一行在这里做什么,因为我在工厂示例中做了,如果你感到困惑,请重新阅读工厂示例。
app.service('myService', function($http, $q){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
});
现在,我们将把控制器中所有可用的方法都附加到this上。
app.service('myService', function($http, $q){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
this.setArtist = function(artist){
_artist = artist;
}
this.getArtist = function(){
return _artist;
}
this.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
});
现在,就像在我们的工厂中一样,setArtist, getArtist和callItunes将在我们传递myService到的任何控制器中可用。这里是myService控制器(与我们的工厂控制器几乎完全相同)。
app.controller('myServiceCtrl', function($scope, myService){
$scope.data = {};
$scope.updateArtist = function(){
myService.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myService.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
就像我之前提到的,一旦你真正理解了“new”的作用,服务就几乎等同于Angular中的工厂。
工厂模式更灵活,因为它可以返回函数和值以及对象。
恕我直言,在服务模式中没有太多意义,因为它所做的一切都可以通过工厂轻松完成。例外可能是:
如果您出于某种原因关心实例化服务的声明类型——如果您使用服务模式,则构造函数将是新服务的类型。 如果您已经有一个正在其他地方使用的构造函数,并且还想将其用作服务(尽管如果您想向其中注入任何东西,可能没有多大用处!)。
Arguably, the service pattern is a slightly nicer way to create a new object from a syntax point of view, but it's also more costly to instantiate. Others have indicated that angular uses "new" to create the service, but this isn't quite true - it isn't able to do that because every service constructor has a different number of parameters. What angular actually does is use the factory pattern internally to wrap your constructor function. Then it does some clever jiggery pokery to simulate javascript's "new" operator, invoking your constructor with a variable number of injectable arguments - but you can leave out this step if you just use the factory pattern directly, thus very slightly increasing the efficiency of your code.
我花了一些时间试图弄清楚其中的区别。
我认为工厂函数使用模块模式,服务函数使用标准的java脚本构造函数模式。
angular.service('myService', myServiceFunction);
angular.factory('myFactory', myFactoryFunction);
我很难理解这个概念,直到我这样对自己说:
Service:你所写的函数将被new-ed:
myInjectedService <---- new myServiceFunction()
Factory:你写的函数(构造函数)将被调用:
myInjectedFactory <--- myFactoryFunction()
你怎么做取决于你自己,但有一些有用的模式……
比如写一个服务函数来公开一个公共API:
function myServiceFunction() {
this.awesomeApi = function(optional) {
// calculate some stuff
return awesomeListOfValues;
}
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();
或者使用工厂函数公开一个公共API:
function myFactoryFunction() {
var aPrivateVariable = "yay";
function hello() {
return "hello mars " + aPrivateVariable;
}
// expose a public API
return {
hello: hello
};
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.hello = myInjectedFactory.hello();
或者使用工厂函数返回构造函数:
function myFactoryFunction() {
return function() {
var a = 2;
this.a2 = function() {
return a*2;
};
};
}
---------------------------------------------------------------------------------
// Injected in your controller
var myShinyNewObject = new myInjectedFactory();
$scope.four = myShinyNewObject.a2();
用哪一个?
两者都可以达到同样的效果。然而,在某些情况下,工厂可以更灵活地使用更简单的语法创建可注入对象。这是因为myInjectedService必须始终是一个对象,而myInjectedFactory可以是一个对象、一个函数引用或任何值。例如,如果你写了一个服务来创建一个构造函数(就像上面的最后一个例子一样),它必须像这样被实例化:
var myShinyNewObject = new myInjectedService.myFunction()
这可能比这个更不可取:
var myShinyNewObject = new myInjectedFactory();
(但是首先应该警惕使用这种类型的模式,因为在控制器中新建对象会创建难以跟踪的依赖关系,这很难模拟测试。让一个服务为你管理对象集合比随意使用new()更好。
还有一件事,他们都是单身……
还要记住,在这两种情况下,angular都是在帮助你管理单例对象。无论注入服务或函数的位置或次数如何,都将获得对相同对象或函数的相同引用。(当工厂只是返回一个像数字或字符串的值时除外。在这种情况下,你将总是得到相同的值,但不是引用。)