Angular.service vs angular.factory
Я видел как angular.factory() и angular.service() используется для объявления услуг; однако я не могу найти angular.service
в любом месте официальной документации.
В чем разница между этими двумя методами? Что нужно использовать для чего (при условии, что они делают разные вещи)?
Ответы
Ответ 1
angular.service('myService', myServiceFunction);
angular.factory('myFactory', myFactoryFunction);
У меня возникли проблемы, обертывая мою голову вокруг этой концепции, пока я не поставил ее себе следующим образом:
Сервис: функция, которую вы пишете, будет новой -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();
Или используя функцию factory, чтобы открыть открытый 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();
Или с помощью функции factory для возврата конструктора:
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();
Какой из них использовать?...
Вы можете сделать то же самое с обоими. Однако в некоторых случаях factory дает вам немного больше возможностей для создания инъекций с более простым синтаксисом. Это потому, что хотя myInjectedService всегда должен быть объектом, myInjectedFactory может быть объектом, ссылкой на функцию или любым значением вообще. Например, если вы написали сервис для создания конструктора (как в последнем примере выше), он должен быть создан таким образом:
var myShinyNewObject = new myInjectedService.myFunction()
что, возможно, менее желательно, чем это:
var myShinyNewObject = new myInjectedFactory();
(Но вы должны быть осторожны в использовании этого типа шаблонов в первую очередь потому, что новые объекты в ваших контроллерах создают труднодоступные зависимости, которые сложно подделать для тестирования. Лучше иметь сервис, управляющий коллекцией объектов для вас, чем использовать new()
wily-nilly.)
Еще одна вещь, все они синглтоны...
Также имейте в виду, что в обоих случаях angular помогает вам управлять синглом. Независимо от того, где и сколько раз вы вводите свою услугу или функцию, вы получите ту же ссылку на тот же объект или функцию. (За исключением того, что factory просто возвращает значение, как число или строку. В этом случае вы всегда получите одно и то же значение, но не ссылку.)
Ответ 2
Проще говоря..
// Service
service = (a, b) => {
a.lastName = b;
return a;
};
// Factory
factory = (a, b) => Object.assign({}, a, { lastName: b });
const fullName = { firstName: 'john' };
// Service
const lastNameService = (a, b) => {
a.lastName = b;
return a;
};
console.log(lastNameService(fullName, 'doe'));
// Factory
const lastNameFactory = (a, b) =>
Object.assign({}, a, { lastName: b })
console.log(lastNameFactory(fullName, 'doe'));
Ответ 3
Вот основные отличия:
Сервисы
Синтаксис: module.service( 'serviceName', function );
Результат: при объявлении serviceName в качестве аргумента для инъекции вам будет предоставлен экземпляр функции, переданной в module.service
.
Использование: Может быть полезно для совместного использования служебных функций, которые полезны для вызова, просто добавляя ( )
к ссылке с введенной функцией. Также может быть запущен с injectedArg.call( this )
или аналогичным.
Заводы
Синтаксис: module.factory( 'factoryName', function );
Результат: при объявлении factoryName в качестве аргумента для инъекции вам будет предоставлено значение, которое возвращается, вызывая ссылку на функцию, переданную module.factory
.
Использование: Может быть полезно вернуть функцию класса, которая затем может быть создана для создания экземпляров.
Вот пример использования сервисов и фабрики. Узнайте больше об услугах AngularJS и Factory.
Вы также можете проверить документацию AngularJS и аналогичный вопрос на stackoverflow, запутанный об обслуживании и фабрике.
Ответ 4
TL; DR
1). Когда вы используете Factory, вы создаете объект, добавляете к нему свойства, а затем возвращаете тот же объект. Когда вы передадите этот factory в свой контроллер, эти свойства объекта теперь будут доступны в этом контроллере через ваш 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). Когда вы используете Сервис, Angular создает за кулисами новое ключевое слово. Из-за этого вы добавите свойства, чтобы "это, и служба вернет" это. Когда вы передаете услугу в свой контроллер, эти свойства "теперь будут доступны на этом контроллере через вашу службу".
app.controller('myServiceCtrl', function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service('myService', function(){
var _artist = 'Nelly';
this.getArtist = function(){
return _artist;
}
});
Non TL; DR
1) Factory
Фабрики - самый популярный способ создания и настройки сервиса. Theres действительно не намного больше, чем TL, сказал DR. Вы просто создаете объект, добавляете к нему свойства, а затем возвращаете тот же объект. Затем, когда вы передаете factory в свой контроллер, эти свойства объекта теперь будут доступны в этом контроллере через ваш factory. Ниже приведен более обширный пример.
app.factory('myFactory', function(){
var service = {};
return service;
});
Теперь любые свойства, которые мы прикрепляем к сервису, будут доступны нам, когда мы передадим myFactory в наш контроллер.
Теперь добавим некоторые частные переменные в нашу функцию обратного вызова. Они не будут напрямую доступны из контроллера, но мы в конечном итоге настроим некоторые методы 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;
});
Здесь вы заметили, что эти функции/функции не были привязаны к сервису. Просто создавали их, чтобы впоследствии использовать или модифицировать их.
- baseUrl - это базовый URL, который требуется API-интерфейсу iTunes.
- _artist - художник, которого мы хотим найти
- _finalUrl - это окончательный и полностью созданный URL-адрес, который хорошо выполняет вызов iTunes. makeUrl - это функция, которая будет создавать и возвращать наши
iTunes дружественный URL.
Теперь, когда наши вспомогательные/частные переменные и функции находятся на месте, добавьте некоторые свойства в объект службы. Независимо от того, что мы надеемся на "хорошее обслуживание", мы сможем напрямую использовать в любом контроллере, который мы передаем "myFactory" .
Мы собираемся создавать методы setArtist и getArtist, которые просто возвращают или устанавливают исполнителя. Мы также создадим метод, который вызовет iTunes API с нашим созданным URL. Этот метод вернет обещание, которое будет выполнено после того, как данные вернутся из iTunes API. Если у вас не было большого опыта использования promises в Angular, я настоятельно рекомендую глубоко погрузиться в них.
Ниже setArtist принимает художника и позволяет вам установить исполнителя. getArtist возвращает вызов artistItunes, сначала вызывает makeUrl(), чтобы правильно использовать URL с нашим запросом $http. Затем он создает объект обещания, делает запрос $http с нашим окончательным URL-адресом, а потому, что $http возвращает обещание, мы можем вызвать .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;
});
Теперь наш factory завершен. Теперь мы можем ввести "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);
})
}
});
В вышеприведенном контроллере вводились инъекции в службу "myFactory" . Затем мы устанавливаем свойства на нашем объекте $scope, который поступает из данных из 'myFactory. Единственный сложный код, приведенный выше, заключается в том, что раньше вы не рассматривали promises. Поскольку callItunes возвращает обещание, мы можем использовать метод .then() и устанавливать значение $scope.data.artistData только после того, как наше обещание будет выполнено с данными iTunes. Вы заметите, что наш контроллер очень "тонкий". Все наши логические и постоянные данные находятся в нашем сервисе, а не в нашем контроллере.
2) Сервис
Возможно, самое большое, что нужно знать при создании службы, - это то, что она создавалась с помощью нового ключевого слова. Для вас JavaScript-гуру это должно дать вам большой намек на природу кода. Для тех из вас, кто имеет ограниченный фон в JavaScript или для тех, кто не знаком с тем, что на самом деле делает "новое ключевое слово", рассмотрим некоторые основы JavaScript, которые в конечном итоге помогут нам понять природу Сервиса.
Чтобы действительно увидеть изменения, возникающие при вызове функции с помощью нового ключевого слова, создайте функцию и вызовите ее с помощью нового ключевого слова, а затем покажите, что делает интерпретатор, когда видит новое ключевое слово. Конечные результаты будут одинаковыми.
Сначала создадим наш конструктор.
var Person = function(name, age){
this.name = name;
this.age = age;
}
Это типичная функция конструктора JavaScript. Теперь всякий раз, когда мы вызываем функцию 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'
Теперь давайте посмотрим, что на самом деле происходит, когда вы используете "новое ключевое слово в JavaScript". Первое, что вы должны заметить, это то, что после использования "нового в нашем примере" удалось вызвать метод (sayName) на "tyler", как если бы это был объект, потому что это так. Итак, сначала мы знаем, что наш конструктор Person возвращает объект, можем ли мы видеть это в коде или нет. Во-вторых, мы знаем, что поскольку наша функция sayName находится на прототипе, а не непосредственно на экземпляре Person, объект, возвращаемый функцией Person, должен делегировать его прототипу при неудачном поиске. В более простых терминах, когда мы вызываем tyler.sayName(), интерпретатор говорит: "ОК, я собираюсь посмотреть на" только что созданный объект tyler ", найти функцию sayName, а затем вызвать его. Подождите, я не вижу его здесь - все, что я вижу, это имя и возраст, позвольте мне проверить прототип. Да, похоже, он на прототипе, позвольте мне называть его".
Ниже приведен код того, как вы можете думать о том, что "новое ключевое слово действительно делает в JavaScript". В основном это пример кода вышеприведенного параграфа. Ive положил "представление интерпретатора или способ, которым интерпретатор видит код внутри заметок".
var Person = function(name, age){
//The line below this creates an obj object that will delegate to the person 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", создание службы в Angular должно быть проще понять.
Самое важное, что нужно понять при создании Сервиса, - это знать, что Сервисы создаются с помощью нового ключевого слова. Объединив это знание с нашими примерами выше, вы должны теперь признать, что вы будете напрямую привязывать свои свойства и методы к "этому, который затем будет возвращен из самой службы. Давайте взглянем на это в действии.
В отличие от того, что мы изначально сделали с примером factory, нам не нужно создавать объект, а затем возвращать этот объект, потому что, как уже упоминалось много раз ранее, мы использовали ключевое слово 'new, чтобы интерпретатор создавал этот объект, делегировать его прототипу, а затем вернуть его нам без необходимости выполнять эту работу.
Прежде всего, давайте создадим нашу "частную" и вспомогательную функцию. Это должно выглядеть очень хорошо, поскольку мы сделали то же самое с нашим factory. Я не буду объяснять, что делает каждая строка, потому что я сделал это в примере factory, если вы запутались, перечитайте пример factory.
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;
}
});
Теперь приложите все наши методы, которые будут доступны в нашем контроллере для этого.
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;
}
});
Теперь, как и в нашем factory, setArtist, getArtist и callItunes будут доступны в том, какой контроллер мы передаем myService. Heres контроллер myService (который почти точно совпадает с нашим контроллером factory).
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);
})
}
});
Как я уже упоминал ранее, когда вы действительно понимаете, что нового, службы почти идентичны фабрикам в Angular.
Ответ 5
Ключ в имени
Службы и заводы похожи друг на друга. Оба будут давать одноэлементный объект, который может быть введен в другие объекты, поэтому они часто используются взаимозаменяемо.
Они предназначены для использования семантически для реализации различных шаблонов проектирования.
Сервисы предназначены для реализации шаблона обслуживания
Шаблон службы - это тот, в котором ваше приложение разбито на логически согласованные единицы функциональности. Примером может быть API-интерфейс или набор бизнес-логики.
Это особенно важно в Angular, потому что Angular модели обычно являются объектами JSON, вытащенными с сервера, и поэтому нам нужно где-то поставить нашу бизнес-логику.
Вот пример Github, например. Он знает, как разговаривать с Гитубом. Он знает о URL-адресах и методах. Мы можем ввести его в контроллер, и он будет генерировать и возвращать обещание.
(function() {
var base = "https://api.github.com";
angular.module('github', [])
.service('githubService', function( $http ) {
this.getEvents: function() {
var url = [
base,
'/events',
'?callback=JSON_CALLBACK'
].join('');
return $http.jsonp(url);
}
});
)();
Заводы реализуют шаблон factory
Заводы, с другой стороны, предназначены для реализации шаблона factory. A factory в одном, в котором мы используем функцию factory для генерации объекта. Обычно мы можем использовать это для построения моделей. Вот factory, который возвращает конструктор Author:
angular.module('user', [])
.factory('User', function($resource) {
var url = 'http://simple-api.herokuapp.com/api/v1/authors/:id'
return $resource(url);
})
Мы использовали бы это так:
angular.module('app', ['user'])
.controller('authorController', function($scope, User) {
$scope.user = new User();
})
Обратите внимание, что фабрики также возвращают одиночные числа.
Заводы могут возвращать конструктор
Поскольку объект factory просто возвращает объект, он может возвращать любой тип объекта, который вам нравится, включая функцию конструктора, как мы видим выше.
Заводы возвращают объект; услуги являются новыми
Еще одно техническое отличие заключается в том, как скомпилированы сервисы и фабрики. Для генерации объекта будет введена служебная функция. Будет вызываться функция factory и вернет объект.
- Службы - это новые конструкторы.
- Заводы просто вызываются и возвращают объект.
Это означает, что в службе мы добавляем к "this", который в контексте конструктора укажет на объект, находящийся в разработке.
Чтобы проиллюстрировать это, вот тот же простой объект, созданный с использованием службы, и factory:
angular.module('app', [])
.service('helloService', function() {
this.sayHello = function() {
return "Hello!";
}
})
.factory('helloFactory', function() {
return {
sayHello: function() {
return "Hello!";
}
}
});
Ответ 6
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', поскольку он был создан с использованием "нового".
app.service('fn', function () {
var foo = 0;
var bar = 0;
this.setFoo = function (val) {
foo = val;
}
this.setBar = function (val){
bar = val;
}
});
Заключение
Когда дело доходит до использования фабрик или сервисов, они оба очень похожи. Они вводятся в контроллеры, директивы, блок запуска и т.д. И используются в клиентском коде практически так же. Они также являются однополыми, то есть один и тот же экземпляр разделяется между всеми местами, где вводится услуга / factory.
Итак, что вы предпочитаете? Либо одно - они настолько похожи, что различия тривиальны. Если вы выбираете один над другим, просто знайте, как они построены, чтобы вы могли правильно их реализовать.
Ответ 7
Все ответы здесь, кажется, вокруг сервиса и factory, и это действительно так, поскольку об этом спрашивали. Но также важно помнить, что есть несколько других, включая provider()
, value()
и constant()
.
Ключом к запоминанию является то, что каждый из них является частным случаем другого. Каждый специальный случай по цепочке позволяет вам делать то же самое с меньшим количеством кода. Каждый из них также имеет некоторые дополнительные ограничения.
Чтобы решить, когда использовать, который вы видите, какой из них позволяет вам делать то, что вы хотите, меньше кода. Вот изображение, иллюстрирующее, насколько они похожи:
Для полной пошаговой разбивки и быстрой ссылки, когда использовать каждый, вы можете посетить сообщение в блоге, где я получил это изображение:
http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/
Ответ 8
Я потратил некоторое время, пытаясь понять разницу.
И я думаю, что функция factory использует шаблон модуля, а функция сервиса использует стандартный шаблон конструктора java script.
Ответ 9
Шаблон factory более гибкий, так как он может возвращать функции и значения, а также объекты.
В шаблоне сервиса IMHO нет много смысла, так как все, что он делает, вы можете так же легко сделать с помощью factory. Исключения могут быть:
- Если вы позаботились о объявленном типе своего созданного вами сервиса по какой-либо причине - если вы используете шаблон службы, ваш конструктор будет типом новой службы.
- Если у вас уже есть функция-конструктор, которую вы используете в другом месте, которую вы также хотите использовать в качестве службы (хотя, вероятно, не так много, если вы хотите что-то в нее вставить!).
Возможно, шаблон обслуживания - это немного более удобный способ создания нового объекта с точки зрения синтаксиса, но он также более дорогостоящий для создания экземпляра. Другие указали, что angular использует "новый" для создания сервиса, но это не совсем так - он не может этого сделать, потому что каждый конструктор службы имеет различное количество параметров. Фактически angular использует шаблон factory внутри, чтобы обернуть вашу конструкторскую функцию. Затем он делает какую-то хитроумную игрушку для моделирования javascript "нового" оператора, ссылаясь на ваш конструктор с переменным количеством вводимых аргументов, но вы можете отказаться от этого шага, если вы просто используете шаблон factory напрямую, тем самым очень немного повышая эффективность вашего кода.