angular.service против angular.factory


1065

Я видел как angular.factory (), так и angular.service (), используемые для объявления сервисов; Однако я не могу найти angular.service нигде в официальной документации.

В чем разница между этими двумя методами?
Что следует использовать для чего (при условии, что они делают разные вещи)?



4
Я искал "[angularjs] service factory", но я также вспомнил, что уже был вопрос об этом (потому что я думал о том, чтобы написать этот / этот вопрос сам в какой-то момент).
Марк Райкок

2
В поиске обозначают ли квадратные скобки тег?
Джейкоб

11
Квадратные скобки @Jacob сужают ваш поиск. Директивы [angularjs] - будут искать «директивы» для вопросов, уже помеченных angularjs.
Махбуб

1
@Mahbub Другими словами, «да» :)
Брайан

Ответы:


1268
  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();

Или используя фабричную функцию для предоставления общедоступного 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();

(Но вы должны быть осторожны с использованием этого типа шаблона в первую очередь, потому что новые объекты -ing в ваших контроллерах создают трудно отслеживаемые зависимости, которые трудно смоделировать для тестирования. Лучше иметь службу, управляющую коллекцией объектов для Вы чем пользуетесь new()коварно.)


Еще одна вещь, они все синглтоны ...

Также имейте в виду, что в обоих случаях angular помогает вам управлять одиночкой. Независимо от того, где и сколько раз вы вводите свой сервис или функцию, вы получите одну и ту же ссылку на один и тот же объект или функцию. (За исключением случая, когда фабрика просто возвращает значение, такое как число или строка. В этом случае вы всегда получите одно и то же значение, но не ссылку.)


2
Было бы лучше назвать его конструктором объекта, чем Newable?
марксизм

2
@ Гуго, я демонстрировал, что вы можете эффективно выполнять одно и то же с обоими, просто синтаксис будет отличаться.
Джил Бирман

105
Я не уверен, сколько раз мне придется читать о разнице между обслуживанием и фабрикой, прежде чем я буду убежден, что оба они необходимы
DMac the Destroyer 15.12.14

10
У нас уже есть глагол, чтобы сказать «новому», это «экземпляр». Просто для справки. :)
sscarduzio

7
Фабрики - это функции, которые вызываются, поэтому они могут возвращать что угодно. С другой стороны, службы создаются с помощью angular via new fn(), поэтому они должны возвращать экземпляр.
Гил Бирман,

318

Проще говоря ..

// 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'));


169
Чувак, спасибо Не то, чтобы детали других ответов были недействительными, но иногда вам нужна 10-секундная версия.
R Claven

4
Просто сервисная функция ничего не возвращает. This.name = ... достаточно , чтобы показать , что она подвергая API.
pixelbits

3
Однако, если вы вернетесь и возразите, он будет использовать это вместо этого. jsfiddle.net/Ne5P8/1221
MrB

@MrB, это обычная функция JavaScript, не специфичная для Angular или контекста этого вопроса.
Ом Шанкар

@ Ом Шанкар, Ответ выше показывает, что разница заключается в использовании этого против возвращаемого объекта. Я показал, что «ЭТО» - это значение по умолчанию, которое будет использоваться со службой, однако, если вы вернете значение, оно будет действовать почти так же, как фабрика. Однако, с другой стороны, кажется, что фабрика требует возвращаемого значения, иначе произойдет ошибка - (показано в этом примере - jsfiddle.net/hmoc0q3v/1 ).
MrB

247

Вот основные отличия:

Сервисы

Синтаксис: module.service( 'serviceName', function );

Результат: при объявлении serviceName в качестве вводимого аргумента вам будет предоставлен экземпляр функции, переданной в module.service.

Использование: Может быть полезно для совместного использования служебных функций , которые полезно вызывать, просто добавляя ( )ссылку на введенную функцию. Может также работать с injectedArg.call( this )или аналогичным.

Заводы

Синтаксис: module.factory( 'factoryName', function );

Результат: при объявлении factoryName в качестве вводимого аргумента вам будет предоставлено значение, возвращаемое путем вызова ссылки на функцию, переданной в module.factory.

Использование: Может быть полезно для возврата функции 'class', которая затем может быть new'ed для создания экземпляров.

Вот пример использования сервисов и фабрики . Узнайте больше о AngularJS Service vs Factory .

Вы также можете проверить документацию AngularJS и аналогичный вопрос о стековом потоке, перепутанный с сервисом против фабрики .


27
Я не согласен с вашим примером использования фабрики. И сервисы, и фабрики (при условии, что функция возвращается. Это может быть просто значение или объект) могут быть новыми. На самом деле сервис - это единственный вариант, который гарантированно станет новым, так как вам предоставляется экземпляр функции. Я бы сказал, что преимущество использования FACTORY по сравнению с SERVICE заключается в том, что оно позволяет некоторый контроль над доступом к свойствам - частным и общедоступным per se, тогда как все свойства службы доступны по своей природе. И я думаю о провайдере как о фабрике фабрики - только ее можно вводить и настраивать во время конфигурирования.
Дрю R

1
@DrewR Спасибо за ваш комментарий, я нашел хороший пример публичных и приватных методов с использованием Factory: stackoverflow.com/a/14904891/65025
edzillion

Я должен согласиться с @DrewR на самом деле. Раньше я использовал фабрики для возврата объектов, но, честно говоря, на этом этапе стоит использовать $providersвсе время.
jedd.ahyoung

служба автоматически создает экземпляр конструктора, верно?
Martian2049

1
@DrewR - Насколько я понимаю, это правда, что вы можете добиться такого же нового эффекта от сервиса, как вы можете на заводе, но это не то, для чего он предназначен. Его главная цель - когда вы просто хотите вернуть некоторый служебный объект и для этого он предоставляет более подходящий синтаксис - вы можете просто писать this.myFunc = function(){}в своем сервисе (избавляет вас от написания кода для создания объекта, как вы должны делать с фабрикой ).
BornToCode

137

TL; DR

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) Когда вы используете Сервис , 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; DR

1)
Фабрики - это самый популярный способ создания и настройки сервиса. Там действительно не намного больше, чем то, что сказал TL; DR. Вы просто создаете объект, добавляете к нему свойства, а затем возвращаете тот же объект. Затем, когда вы передадите фабрику в свой контроллер, эти свойства объекта теперь будут доступны в этом контроллере через вашу фабрику. Более подробный пример приведен ниже.

app.factory('myFactory', function(){
  var service = {};
  return service;
});

Теперь любые свойства, которые мы прикрепляем к «сервису», будут нам доступны, когда мы передадим «myFactory» в наш контроллер.

Теперь давайте добавим некоторые «приватные» переменные в нашу функцию обратного вызова. Они не будут напрямую доступны из контроллера, но мы в конечном итоге настроим некоторые методы получения / установки для «службы», чтобы иметь возможность изменять эти «частные» переменные при необходимости.

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 - это базовый URL, который требуется iTunes API
  • _artist - художник, которого мы хотим найти
  • _finalUrl - это окончательный и полностью созданный URL-адрес, по которому мы будем обращаться к iTunes. makeUrl - это функция, которая создает и возвращает наш удобный для URL-адрес iTunes.

Теперь, когда наши вспомогательные / закрытые переменные и функции на месте, давайте добавим некоторые свойства к объекту 'service'. Что бы мы ни ставили на «сервис», мы сможем напрямую использовать тот контроллер, в который мы передаем «myFactory».

Мы собираемся создать методы setArtist и getArtist, которые просто возвращают или устанавливают художника. Мы также собираемся создать метод, который будет вызывать iTunes API с нашим созданным URL. Этот метод возвращает обещание, которое будет выполнено, как только данные вернутся из iTunes API. Если у вас не было большого опыта использования обещаний в Angular, я настоятельно рекомендую глубоко погрузиться в них.

Ниже setArtist принимает художника и позволяет установить художника. getArtist возвращает художника callItunes сначала вызывает 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;
});

Теперь наша фабрика завершена. Теперь мы можем внедрить '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. Единственный сложный код выше, если вы никогда не имели дело с обещаниями раньше. Поскольку callItunes возвращает обещание, мы можем использовать метод .then () и устанавливать $ scope.data.artistData только после того, как наше обещание будет выполнено с данными iTunes. Вы заметите, что наш контроллер очень «тонкий». Вся наша логика и постоянные данные находятся в нашем сервисе, а не в нашем контроллере.

2) Сервис
Возможно, самая важная вещь, которую нужно знать при создании Сервиса, это то, что он создается с ключевым словом «new». Для вас, гуру JavaScript, это должно дать вам подсказку о природе кода. Для тех из вас, кто имеет ограниченный опыт работы с JavaScript, или тех, кто не слишком знаком с тем, что на самом деле делает ключевое слово «new», давайте рассмотрим некоторые основы JavaScript, которые в конечном итоге помогут нам понять природу Сервиса.

Чтобы действительно увидеть изменения, которые происходят при вызове функции с ключевым словом «new», давайте создадим функцию и вызовем ее с ключевым словом «new», а затем покажем, что делает интерпретатор, когда видит ключевое слово «new». Конечные результаты будут одинаковыми.

Сначала давайте создадим наш конструктор.

var Person = function(name, age){
  this.name = name;
  this.age = age;
}

Это типичная функция конструктора JavaScript. Теперь всякий раз, когда мы вызываем функцию Person с помощью ключевого слова new, 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'

Теперь давайте посмотрим, что на самом деле происходит, когда вы используете ключевое слово «new» в JavaScript. Первое, что вы должны заметить, это то, что после использования «new» в нашем примере мы можем вызвать метод (sayName) для «tyler» так же, как если бы это был объект - так оно и есть. Итак, во-первых, мы знаем, что наш конструктор Person возвращает объект, видим ли мы это в коде или нет. Во-вторых, мы знаем, что поскольку наша функция sayName расположена в прототипе, а не непосредственно в экземпляре Person, объект, который возвращает функция Person, должен делегировать своему прототипу при неудачных поисках. Проще говоря, когда мы вызываем tyler.sayName (), интерпретатор говорит: «Хорошо, я собираюсь посмотреть на только что созданный объект« tyler », найти функцию sayName, а затем вызвать ее. Подожди, я не вижу этого здесь - все, что я вижу, это имя и возраст, позвольте мне проверить прототип. Да, похоже, что это на прототипе, позвольте мне назвать это ».

Ниже приведен код того, как вы можете думать о том, что на самом деле делает ключевое слово «новый» в 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;
}

Теперь, имея представление о том, что на самом деле делает ключевое слово «new» в JavaScript, создание службы в Angular должно быть проще для понимания.

Самая важная вещь, которую нужно понять при создании Сервиса, это знание того, что Сервисы создаются с помощью ключевого слова «new». Объединяя эти знания с нашими примерами выше, вы теперь должны понимать, что вы будете привязывать свои свойства и методы непосредственно к «this», которое затем будет возвращено из самой Службы. Давайте посмотрим на это в действии.

В отличие от того, что мы изначально делали с примером Factory, нам не нужно создавать объект, а затем возвращать этот объект, потому что, как упоминалось много раз ранее, мы использовали ключевое слово «new», поэтому интерпретатор создаст этот объект, предоставив ему делегирование это прототип, а затем верните его нам без необходимости выполнять работу.

Прежде всего, давайте создадим нашу «приватную» и вспомогательную функцию. Это должно выглядеть очень знакомо, так как мы сделали то же самое с нашим заводом. Я не буду объяснять, что здесь делает каждая строка, потому что я сделал это в заводском примере, если вы не уверены, перечитайте заводской пример.

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);
      })
  }
});

Как я уже упоминал ранее, когда вы действительно понимаете, что делает «новое», Сервисы практически идентичны фабрикам в Angular.


12
Возможно, вы захотите предоставить ссылку непосредственно на ваш блог. tylermcginnis.com/angularjs-factory-vs-service-vs-provider Я обнаружил, что немного легче читать.
Тайлер Кольер

3
Нет ничего плохого в том, чтобы повторять ваш блог здесь, но я согласен, что это отличный пост в блоге.
R Claven

5
Хорошее подробное объяснение того, что каждый из них делает под капотом, но все еще неясно, почему и когда кто-то решит использовать Сервис вместо Фабрики. Другими словами, когда я предпочитаю иметь новый объект против того, который был возвращен фабрикой. Я думаю, что это самая большая путаница.
demisx

2
По сути, если вы хотите создать постоянное соединение с удаленной службой, как, например, API iTunes, упомянутый в примере с постоянным соединением (состояние соединения, история вызовов, хранение данных), вы можете использовать Factory. Если вы реализуете это как Сервис, то каждый раз, когда вам понадобится что-то от API, вы будете заново создавать соединение и не сможете ничего в нем сохранить. Потому что каждый раз, когда вы заново создаете сервис, вы получаете пустой объект / объект по умолчанию.
Meki

4
Я не думаю, что это правильно, @Aznim. Как и другие говорили, оба предоставляют синглтоны.
Криптовирус

35

Ключ в названии

Услуги и фабрики похожи друг на друга. Оба будут давать одноэлементный объект, который может быть введен в другие объекты, и поэтому часто используются взаимозаменяемо.

Они предназначены для семантического использования для реализации различных шаблонов проектирования.

Сервисы для реализации сервисного шаблона

Шаблон службы - это шаблон, в котором ваше приложение разбито на логически согласованные единицы функциональности. Примером может быть средство доступа API или набор бизнес-логики.

Это особенно важно в Angular, потому что Angular модели, как правило, представляют собой просто объекты JSON, извлеченные с сервера, и поэтому нам нужно где-то разместить нашу бизнес-логику.

Вот сервис Github, например. Он умеет разговаривать с 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);
      }
    });
  )();

Фабрики реализуют фабричный образец

Фабрики, с другой стороны, предназначены для реализации фабричного образца. Фабричный шаблон, в котором мы используем фабричную функцию для генерации объекта. Обычно мы можем использовать это для построения моделей. Вот фабрика, которая возвращает конструктор 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();
  })

Обратите внимание, что фабрики также возвращают синглтоны.

Фабрики могут вернуть конструктор

Поскольку фабрика просто возвращает объект, она может вернуть любой тип объекта, который вам нравится, включая функцию конструктора, как мы видели выше.

Фабрики возвращают объект; услуги являются новыми

Другое техническое отличие состоит в том, как складываются услуги и фабрики. Сервисная функция будет обновлена ​​для создания объекта. Будет вызвана фабричная функция, которая вернет объект.

  • Сервисы - это новые конструкторы.
  • Фабрики просто называются и возвращают объект.

Это означает, что в сервисе мы добавляем «this», которое в контексте конструктора будет указывать на строящийся объект.

Чтобы проиллюстрировать это, вот тот же простой объект, созданный с использованием сервиса и фабрики:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });

2
отличное объяснение, спасибо! также есть пример кода примера фабрики, где Authorдолжен быть параметр инжектора Person.
Михаил-т

Спасибо @ mik-T, я исправил опечатки.
суперсветовой

1
Вы используете шаблон обслуживания неправильно - это должно быть фабрикой. Если вы вызовете .factory () вместо .service (), вы увидите, что он работает точно так же. Сервисный шаблон предназначен для предоставления функции конструктора, а не функции, которая возвращает новый объект. Angular (эффективно) вызывает «new» в вашей функции конструктора. Единственная причина, по которой работает ваш сервис, заключается в том, что если вы вызываете «new» в функции конструктора, которая возвращает объект, вы фактически возвращаете возвращенный объект, а не созданный. И фабрики могут быть использованы для создания чего угодно, а не только моделей.
Дэн Кинг,

27

Все ответы здесь, похоже, касаются обслуживания и фабрики, и это действительно так, потому что об этом спрашивали. Но это также важно иметь в виду , что есть несколько других , включая provider(), value()и constant().

Ключ для запоминания заключается в том, что каждый из них является частным случаем другого. Каждый особый случай в цепочке позволяет вам делать то же самое с меньшим количеством кода. Каждый из них также имеет некоторые дополнительные ограничения.

Чтобы решить, когда использовать, который вы видите, какой из них позволяет вам делать то, что вы хотите в меньшем количестве кода. Вот изображение, иллюстрирующее, насколько они похожи:

введите описание изображения здесь

Для полной пошаговой разбивки и быстрого ознакомления с тем, когда использовать каждый, вы можете посетить блог, где я получил это изображение:

http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/


3
@jacob может быть и так, но я думаю, что общая концепция не только о том, когда использовать каждый, но о том, что все они по сути являются вариациями одного и того же, является важной.
Луис Перес

1
@LuisPerez Ссылка на ваш блог и видео, объясняющие разницу, это действительно здорово. Это легче понять с помощью этих примеров из видео :)
Алин Чокан

24

app.factory ('fn', fn) против 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;
         }
});

Вывод

Когда дело доходит до использования заводов или услуг, они оба очень похожи. Они внедряются в контроллеры, директивы, блоки выполнения и т. Д. И используются в клиентском коде почти таким же образом. Они также являются синглетонами - это означает, что один и тот же экземпляр является общим для всех мест, где вводится сервис / фабрика.

Итак, что вы предпочитаете? Либо один - они настолько похожи, что различия тривиальны. Если вы выбираете одно над другим, просто знайте, как они построены, чтобы вы могли правильно их реализовать.


Сервисные функции не «ничего не возвращают», они неявно возвращают созданный объект, ЕСЛИ вы не указываете свой собственный оператор возврата (в последнем случае возвращаемый объект - это то, что будет создано и кэшировано, подобно фабрике).
Криптовирус

Я думаю, что вы неверно истолковываете это ... Когда я говорю «возврат», я имею в виду с точки зрения реализации сервисной функции
pixelbits

ты уверен, что фабрика тоже один город?
Martian2049

5

Я провел некоторое время, пытаясь выяснить разницу.

И я думаю, что фабричная функция использует шаблон модуля, а сервисная функция использует стандартный шаблон конструктора сценариев Java.


2

Фабричный шаблон является более гибким, поскольку он может возвращать функции и значения, а также объекты.

ИМХО, в шаблоне обслуживания нет особого смысла, так как все, что он делает, вы можете легко сделать на фабрике. Исключения могут быть:

  • Если по какой-то причине вы заботитесь об объявленном типе своего экземпляра службы - если вы используете шаблон службы, то ваш конструктор будет типом новой службы.
  • Если у вас уже есть функция конструктора, которую вы используете в другом месте, которую вы также хотите использовать в качестве службы (хотя, вероятно, не очень полезная, если вы хотите что-то внедрить в нее!).

Можно утверждать, что шаблон службы - это немного более приятный способ создания нового объекта с точки зрения синтаксиса, но его создание также более затратно. Другие указали, что angular использует «new» для создания сервиса, но это не совсем так - он не может этого сделать, потому что каждый конструктор сервиса имеет разное количество параметров. На самом деле Angular использует внутренний шаблон фабрики, чтобы обернуть вашу функцию конструктора. Затем он делает несколько хитрых покерных манипуляций для имитации «нового» оператора javascript, вызывая ваш конструктор с переменным числом вводимых аргументов - но вы можете пропустить этот шаг, если вы просто используете фабричный шаблон напрямую, таким образом, очень немного увеличивая эффективность вашего код.


Услуги являются более эффективными для построения, чем фабрики, поскольку фабрики используют относительно дорогие закрытия, а услуги (классы) могут использовать преимущества прототипа.
Джейкоб

@jacob Не уверен, что ты имеешь в виду по поводу замыканий? Фабрика - это просто функция, которая возвращает объект. Вы должны использовать замыкание только в том случае, если возвращаемый объект требует «частного» состояния. Вам все равно придется делать то же самое, если вы используете конструктор (сервис). Я принимаю вашу точку относительно прототипа , хотя - хотя вы могли бы еще сделать это на заводе , если вы хотели.
Дэн Кинг,

function MyFactory(dep1) { var $$foo = 'bar', factory = {}; Object.defineProperties(factory.prototype, { foo: { value: $$foo } }); return factory; } function MyService(dep1) { var $$foo = 'bar'; Object.defineProperties(MyService.prototype, { foo: { value: $$foo } }); } Хотя и MyFactory, и MyService используют прототип, MyFactory по-прежнему испытывает снижение производительности, создавая возвращаемый объект. В обоих примерах у них есть ряды, но в MyService нет разницы в производительности.
Джейкоб

1
Для меня разница в том, хочу ли я использовать фабрику напрямую без метода: MyFactory(someArgument)(ex$http() ). Это не возможно с услугой , как вы бы ссылающегося конструктор: MyService(someArgument).
Джейкоб

Во время создания объекта я не вижу, как factory = {} влияет на производительность, а не на javascript, инициализирующий «this» для вас, когда он вызывает ваш конструктор? И я думаю, что больший удар по производительности происходит с угловой стороны, когда он оборачивает ваш конструктор на фабрике, а затем вынужден перепрыгивать через обручи, чтобы смоделировать «новый», чтобы он мог внедрить ваши зависимости.
Дэн Кинг,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.