Функция конструктора против функций фабрики - PullRequest
133 голосов
/ 02 января 2012

Может кто-нибудь прояснить разницу между функцией конструктора и фабричной функцией в Javascript.

Когда использовать один вместо другого?

Ответы [ 7 ]

140 голосов
/ 02 января 2012

Основное отличие состоит в том, что функция конструктора используется с ключевым словом new (что заставляет JavaScript автоматически создавать новый объект, устанавливать this в функции для этого объекта и возвращать объект):

var objFromConstructor = new ConstructorFunction();

Заводская функция вызывается как «обычная» функция:

var objFromFactory = factoryFunction();

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

В очень простом примере функции, упомянутые выше, могут выглядеть примерно так:

function ConstructorFunction() {
   this.someProp1 = "1";
   this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };

function factoryFunction() {
   var obj = {
      someProp1 : "1",
      someProp2 : "2",
      someMethod: function() { /* whatever */ }
   };
   // other code to manipulate obj in some way here
   return obj;
}

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

Некоторые люди предпочитают использовать заводские функции для всего лишь потому, что им не нравится не забывать использовать new (РЕДАКТИРОВАТЬ: и это может быть проблемой, потому что без new функция все равно будет работать, но не так, как ожидалось ). Я не рассматриваю это как преимущество: new является основной частью языка, поэтому для меня сознательно избегать его произвольности - можно также избегать других ключевых слов, таких как else.

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

101 голосов
/ 05 января 2013

Преимущества использования конструкторов

  • Большинство книг учат вас использовать конструкторы, а new

  • this относится к новому объекту

  • Некоторым нравится, как читает var myFoo = new Foo();.

1020 * Недостатки * Подробности создания экземпляров просочились в вызывающий API (через требование new), поэтому все вызывающие объекты тесно связаны с реализацией конструктора. Если вам когда-нибудь понадобится дополнительная гибкость фабрики, вам придется реорганизовать все вызывающие абоненты (по общему признанию, исключительный случай, а не правило). Забывание new является такой распространенной ошибкой, вам следует настоятельно рекомендовать добавить шаблонную проверку, чтобы убедиться, что конструктор вызывается правильно (if (!(this instanceof Foo)) { return new Foo() }). РЕДАКТИРОВАТЬ: Начиная с ES6 (ES2015) вы не можете забыть new с конструктором class, иначе конструктор выдаст ошибку. Если вы выполните проверку instanceof, это оставляет двусмысленность относительно того, требуется ли new. На мой взгляд, так не должно быть. Вы фактически замкнули требование new, что означает, что вы можете стереть недостаток # 1. Но тогда у вас есть только фабричная функция во всем, кроме имени , с дополнительным шаблоном, заглавной буквой и менее гибким контекстом this. Конструкторы нарушают принцип Открытия / Закрытия Но моя главная проблема в том, что это нарушает принцип открытия / закрытия. Вы начинаете экспортировать конструктор, пользователи начинают использовать конструктор, а затем понимаете, что вместо этого вам нужна гибкость фабрики (например, для переключения реализации на использование пулов объектов или для создания экземпляров в разных контекстах выполнения или для иметь большую гибкость наследования при использовании прототипа OO). Вы застряли, хотя. Вы не можете внести изменения, не нарушив весь код, который вызывает ваш конструктор, с помощью new. Например, нельзя использовать пулы объектов для повышения производительности. Кроме того, использование конструкторов дает вам обманчивое instanceof, которое не работает во всех контекстах выполнения и не работает, если ваш прототип конструктора заменяется. Также произойдет сбой, если вы начнете возвращать this из вашего конструктора, а затем переключитесь на экспорт произвольного объекта, что вам нужно сделать, чтобы включить фабричное поведение в вашем конструкторе. Преимущества использования фабрики

  • Меньше кода - шаблон не требуется.

  • Вы можете вернуть любой произвольный объект и использовать любой произвольный прототип - это дает вам больше гибкости для создания различных типов объектов, которые реализуют один и тот же API. Например, медиаплеер, который может создавать экземпляры как HTML5, так и флэш-плееров, или библиотека событий, которая может генерировать события DOM или события веб-сокетов. Фабрики также могут создавать объекты в разных контекстах выполнения, использовать преимущества пулов объектов и предоставлять более гибкие модели наследования прототипов.

  • Вам никогда не понадобится переходить с фабрики на конструктор, поэтому рефакторинг никогда не будет проблемой.

  • Нет двусмысленности в использовании new. Не. (Это заставит this вести себя плохо, см. Следующий пункт).

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

  • Нет двусмысленности в том, стоит ли использовать заглавные буквы. Не. Инструменты Lint будут жаловаться, и тогда у вас возникнет искушение попытаться использовать new, и тогда вы отмените описанное выше преимущество.

  • Некоторым людям нравится, как var myFoo = foo(); или var myFoo = foo.create(); читает.

Недостатки

  • new не ведет себя должным образом (см. Выше).Решение: не используйте его.

  • this не относится к новому объекту (вместо этого, если конструктор вызывается с точечной нотацией или квадратной скобкой, например, foo.bar () - this относится к foo - как и любой другой метод JavaScript - см. преимущества).

37 голосов
/ 02 января 2012

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

3 голосов
/ 26 декабря 2018

Пример функции конструктора

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");
  • new создает прототип объекта на User.prototype и вызывает User с созданным объектом в качестве значения this.

  • new обрабатывает выражение аргумента для своего операнда как необязательное:

       let user = new User;
    

    заставит new вызвать User без аргументов.

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

Плюсы и минусы

Объекты, созданные функциями конструктора, наследуют свойства от свойства prototype конструктора,и верните true, используя оператор instanceOf в функции конструктора.

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

Функции конструктора могут быть расширены с помощью ключевого слова extends.

Функции конструктора не могут возвращать null в качестве значения ошибки.Так как это не объектный тип данных, он игнорируется new.

Пример функции фабрики

function User(name, age) {
  return {
    name,
    age,
  }
};

let user = User("Tom", 23);

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

"за" и "против"

легко скрывает сложности реализации создания объекта от вызывающей стороны.Это особенно полезно для функций собственного кода в браузере.

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

InВ простых случаях фабричные функции могут быть простыми по структуре и значению.

Возвращаемые объекты обычно не наследуются от свойства prototype фабричной функции и возвращают false из instanceOf factoryFunction.

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

2 голосов
/ 09 июля 2015

Фабрики "всегда" лучше. При использовании объектно-ориентированных языков

  1. принять решение по контракту (методы и что они будут делать)
  2. Создание интерфейсов, которые предоставляют эти методы (в javascript у вас нет интерфейсов, поэтому вам нужно найти способ проверить реализацию)
  3. Создать фабрику, которая возвращает реализацию каждого требуемого интерфейса.

Реализации (фактические объекты, созданные с помощью new) не предоставляются заводскому пользователю / потребителю. Это означает, что разработчик фабрики может расширять и создавать новые реализации, если он / она не нарушает контракт ... и это позволяет потребителю фабрики просто получать выгоду от нового API без необходимости изменять свой код ... если они использовали новую и приходит «новая» реализация, тогда они должны переходить и менять каждую строку, которая использует «новую», чтобы использовать «новую» реализацию ... с фабрикой их код не изменяется ...

Фабрики - лучше, чем все остальное - основа пружины полностью построена на этой идее.

0 голосов
/ 16 апреля 2019

Эрик Эллиотт очень хорошо разъяснил различия

Но для второго вопроса:

Когда использовать один вместо другого?

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

0 голосов
/ 07 апреля 2015

Фабрики - это слой абстракций, и, как и все абстракции, они имеют сложную стоимость.Когда вы сталкиваетесь с API на основе фабрики, выяснить, что такое фабрика для данного API, может быть сложно для потребителя API.С конструкторами открываемость тривиальна.

При выборе между ctors и фабриками вам нужно решить, оправдывает ли сложность выгода.

Стоит отметить, что конструкторы Javascript могут быть произвольными фабриками, возвращая что-то отличное от this или undefined.Таким образом, в js вы можете получить лучшее из обоих миров - обнаруживаемый API и пул / кеширование объектов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...