угловатый.служба против угловой.фабрика

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

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

9 ответов


  angular.service('myService', myServiceFunction);
  angular.factory('myFactory', myFactoryFunction);

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

сервис: the функции что ты напишешь будет новая-ed:

  myInjectedService  <----  new myServiceFunction()

завод: the функции (конструктор), который вы пишете, будет вызывается:

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

какой из них использовать?...

вы можете сделать то же самое с обоими. Однако, в некоторых случаях завод дает вам немного больше гибкости создать injectable с более простым синтаксисом. Это потому, что, хотя myInjectedService всегда должен быть объектом, myInjectedFactory может быть объектом, ссылкой на функцию или любым значением вообще. Например, если вы написали сервис для создания конструктора (как в последнем примере выше), он должен быть создан следующим образом:

var myShinyNewObject = new myInjectedService.myFunction()

что, возможно, менее желательно, чем это:

var myShinyNewObject = new myInjectedFactory();

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


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

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


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

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

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

услуги

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

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

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

фабрики

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

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

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

здесь пример использования услуг и фабрики. Читать дальше о обслуживание AngularJS против фабрики.

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


TL; DR

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

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

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

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

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

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

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

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

ниже setArtist принимает художника и позволяет установить художника. getArtist возвращает исполнитель callItunes первые вызовы makeUrl () для того, чтобы построить URL-адрес, который мы будем использовать с нашим запросом $http. Затем он устанавливает объект promise, делает запрос $http с нашим окончательным url-адресом, затем, поскольку $http возвращает обещание, мы можем позвонить .успешный или. ошибка после нашего запроса. Затем мы решаем наше обещание с данными 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() и только установить $масштаб.данные.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.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, должен делегироваться своему прототипу при неудачном поиске. Проще говоря, когда мы звоним Тайлеру.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;
}

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

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

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


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

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

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

службы предназначены для реализации шаблона службы

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

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

вот сервис Github, например. Он знает, как разговаривать с Github. Он знает об URL-адресах и методах. Мы можем впрыснуть его в регулятор, и он произведет и возвратит a обещать.

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

фабрики реализуют заводской шаблон

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

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

обратите внимание, что фабрики также возвращаются одиночки.

фабрики могут возвращать конструктор

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

фабрики возвращают объект; услуги являются newable

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

  • услуги newable конструкторов.
  • фабрики просто вызываются и возвращают объект.

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

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

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

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

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

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

enter image description here

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

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


app.фабрика ('fn', fn) против приложения.сервис ('fn', fn)

строительство

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

 //factory
 var obj = fn();
 return obj;

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

  //service
  var obj = new fn();
  return obj;

реализация

заводы обычно возвращает литерал объекта, потому что возвращаемое значение is что впрыснуто в регуляторы, блоки бега, директивы, etc

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

сервисные функции обычно ничего не возвращают. Вместо этого они выполняют инициализацию и предоставляют функции. Функции также могут ссылаться на "это", поскольку оно было построено с использованием "new".

app.service('fn', function () {
         var foo = 0;
         var bar = 0;
         this.setFoo = function (val) {
               foo = val;
         }
         this.setBar = function (val){
               bar = val;
         }
});

вывод

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

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


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

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


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

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

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

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