it-roy-ru.com

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

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

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

1054
jacob
  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();

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


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

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

1264
Gil Birman

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

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

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

Сервисы

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

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

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

Фабрики

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

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

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

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

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

247
Manish Chhabra

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

Здесь вы заметите, что мы не привязываем эти переменные/функцию к "сервису". Мы просто создаем их, чтобы потом использовать или модифицировать их.

  • 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". Объединяя эти знания с нашими примерами выше, вы должны теперь признать, что вы будете привязывать свои свойства и методы непосредственно к "этому", которое затем будет возвращено из самой Службы. Давайте посмотрим на это в действии.

В отличие от того, что мы изначально делали с примером 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;
  }
});

Теперь мы прикрепим все наши методы, которые будут доступны в нашем контроллере, к "этому".

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.

135
Tyler McGinnis

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

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

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

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

Шаблон службы - это шаблон, в котором ваше приложение разбито на логически согласованные единицы функциональности. Примером может быть метод доступа 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!";
      }
    }
  });
35
superluminary

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

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

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

enter image description here

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

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

26
Luis Perez

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

Вывод

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

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

23
pixelbits

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

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

5
ps.

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

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

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

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

2
Dan King