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


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

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

9 1042

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

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

вы можете сделать то же самое с обоими. Однако, в некоторых случаях завод дает вам немного больше гибкости для создания инъекционного с более простым синтаксисом. Это потому, что в то время как 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 в качестве вводимого аргумента вам будет предоставлен значение, которое возвращается при вызове функции reference перешло к module.factory.

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

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

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

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

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

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

мы собираемся создать setArtist и методы getArtist, которые просто возвращают или устанавливают художника. Мы также собираемся создать метод, который будет вызывать iTunes API с нашим созданным 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 возвращает обещание, мы можем использовать его .затем () метод и только набор $масштаб.данные.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-адресах и методах. Мы можем внедрить ее в контроллер, и он будет генерировать и возвращать обещать.

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

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

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

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

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

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

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

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

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

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/

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

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

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

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

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

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

реализация

заводы обычно возвращает литерал объекта, потому что возвращаемое значение и что впрыснуто в регуляторы, блоки бега, директивы, 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
         }
  });

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

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-скрипта.

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

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

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

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