В чем разница между factory и provider в AngularJS (на пальцах)

  • Tutorial
Как-то давно переводил статью «Понимание типов сервисов в AngularJS (constant, value, factory, service, provider)». На практике, конечно, не всё пригождается, поэтому в рамках понижения порога вхождения в Ангуляр, объясню проще.

Никаких фабрик в Ангуляре нет. Есть только провайдеры. Записываются так:

app.provider('$helloWorld', function() {
  return {
    world: 'World',
    $get: function($hello) {
      return $hello + this.world;
    }
  }
});


После этого Ангуляр создаст сервис из метода $get:

$helloWorld = function($hello) {
  return $hello + this.world;
}


и его провайдер для настройки остальных параметров:

$helloWorldProvider = {
  world: 'World'
}


Провайдер мы используем для настройки сервиса (на стадии конфигурации):

app.config(function($helloWorldProvider) {
  $helloWorldProvider.world = 'Piter';
})


Сам сервис — после запуска приложения:

app.controller('MainCtrl', function($scope, $helloWorld) {
  $scope.title = $helloWorld; // $hello + 'Piter'
});


Предположим, что настройки нам не нужны:

app.provider('$helloWorld', function() {
  return {
    $get: function($hello) {
      return $hello + 'World';
    }
  }
});


То, что мы сейчас записали есть ни что иное как фабрика. То же самое можно записать так:

app.factory('$helloWorld', function($hello) {
  return $hello + 'World';
});


Разумеется, и в этом случае Ангуляр создаст $helloWorldProvider, только он будет без параметров — пустым.

Вывод: factory, service, value, constant — всего лишь синтаксический сахар для provider

P.S. $hello это просто какой-то наш сервис, взятый для примера:

app.factory('$hello', function() {
  return 'Hello';
});


P.P.S Шпаргалка

Provider
app.provider('$helloWorld', function() {
  return {
    world: 'World',
    $get: function($hello) {
      return $hello + this.world;
    }
  }
});


Factory
app.factory('$helloWorld', function($hello) {
  return $hello + 'World';
});

// или

app.provider('$helloWorld', function() {
  return {
    $get: function($hello) {
      return $hello + 'World';
    }
  }
});


Service
app.service('$helloWorld', HelloWorldClass);

// или

app.provider('$helloWorld', function() {
  return {
    $get: function($hello) {
      return new HelloWorldClass($hello);
    }
  }
});


Value
app.value('$helloWorld', {greating: 'Hello'});

// или

app.provider('$helloWorld', function() {
  return {
    $get: function() {
      return {greating: 'Hello'};
    }
  }
});


Constant
app.constant('$helloWorld', {world: 'Piter'});

// или

app.provider('$helloWorld', function() {
  return {
    world: 'Piter',
    $get: function() {
        return this; //без $get
    }
  }
});

// В config вызывается без суффикса Provider
app.config(function($helloWorld) { ... })


P.P.P.S
function HelloWorldClass($hello) {
  ...
}

app.service('$helloWorld', HelloWorldClass);

Как Ангуляр узнает, какие параметры передать в конструктор? Дело в том, что HelloWorldClass (да и любая функция в $get) запускается через $injector.invoke(fn), а это такая штука, которая читает код функции, вытаскивает аргументы из скобочек и подтягивает одноименные сервисы ($hello в нашем случае). Всё просто!
  • +27
  • 49,7k
  • 9
Поделиться публикацией

Комментарии 9

    +4
    Открыли Америку =)
      +6
      Да вот, не всем понятно до сих пор :-)
      +2
      От провайдера к фабрике упростили. молодцы… а дальше?
      Один шажок же всего к сервису, и полнее б картинка была.
        +2
        Восполню этот небольшой пробел. :)

        Допустим, у нас есть такой «класс» (условно, помним, что классов в ES5 нет):

        function HelloService(name) {
             this.name = name;
        }
        HelloService.prototype.getGreeting = function() {
             return "Hello, " + (this.name || 'world') + "!";
        }
        


        Пусть у нас будет константа name и фабрика:

        app.constant('name', 'Vasya');
        
        app.factory('$hello', function(name) {
            return new HelloService(name);
        });
        


        То же самое можно записать короче:

        app.service('$hello', HelloService);
        


        где вторым аргументом передается функция-конструктор. При этом сработает inject-магия и значение name для конструктора HelloService автоматически заполнится из константы (разумеется, надо не забывать, что при минификации кода нужно явно прописывать $inject).

        Таким образом, module.service — это еще один синтаксический сахар для простейшего случая фабрики.
          0
          Дописал шпаргалку
            0
            Отлично, спасибо.
          +2
          Очень коротко и очень упрощенно

          var superFunction = function(){}

          var factory = superFunction();

          var service = superFunction;
            +1
            да, все верно. если посмотреть исходный код, то становится ясно — я как то заметку по этому поводу делал.
              +1
              Спасибо, стало понятно что к чему. Еще узнал про value, до этого использовал только constant.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое