Функция конструктора vs Factory функции

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

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

Ответ 1

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

var objFromConstructor = new ConstructorFunction();

Функция A factory называется как "регулярная" функция:

var objFromFactory = factoryFunction();

Но для того, чтобы он считался "factory", ему нужно было бы вернуть новый экземпляр некоторого объекта: вы бы не назвали его функцией "factory", если он просто вернул логическое или что-то еще. Это происходит не автоматически, как с помощью 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;
}

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

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

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

Ответ 2

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

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

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

  • Некоторые люди любят путь var myFoo = new Foo();.

Недостатки

  • Информация о создании экземпляра становится просочившейся в вызывающий API (с помощью требования new), поэтому все вызывающие стороны тесно связаны с реализацией конструктора. Если вам понадобится дополнительная гибкость factory, вам придется реорганизовать всех абонентов (по общему признанию, исключительный случай, а не правило).

  • Забывание new является такой общей ошибкой, вы должны серьезно подумать о добавлении контрольной проверки, чтобы убедиться, что конструктор вызван правильно (if (!(this instanceof Foo)) { return new Foo() }). EDIT: Начиная с ES6 (ES2015), вы не можете забыть new с помощью конструктора class, или конструктор выдает ошибку.

  • Если вы выполняете проверку instanceof, это оставляет неопределенность относительно того, требуется ли new. На мой взгляд, этого не должно быть. Фактически вы коротко замыкаете требование new, что означает, что вы можете удалить недостаток # 1. Но тогда вы только что получили factory функцию во всех, кроме имени, с дополнительным шаблоном, заглавной буквой и менее гибким контекстом this.

Конструкторы нарушают принцип открытого/закрытого

Но моя главная проблема заключается в том, что она нарушает принцип открытого/закрытого. Вы начинаете экспортировать конструктор, пользователи начинают использовать конструктор, а затем по дороге, которую вы понимаете, вам нужна гибкость factory вместо этого (например, чтобы переключить реализацию на использование пулов объектов или создать экземпляр контекстов выполнения, или иметь больше гибкости наследования с использованием прототипального OO).

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

Кроме того, использование конструкторов дает обманчивый instanceof, который не работает во всех контекстах выполнения и не работает, если ваш прототип конструктора поменяется. Он также потерпит неудачу, если вы начнете возвращать this из своего конструктора, а затем переключитесь на экспорт произвольного объекта, который вам нужно будет сделать, чтобы включить factory -подобное поведение в вашем конструкторе.

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

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

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

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

  • Нет двусмысленности в использовании 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 - см. преимущества).

Ответ 3

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

Ответ 4

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

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

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

Заводы - лучше, чем все остальное - инфраструктура spring полностью построена вокруг этой идеи.

Ответ 5

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

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 в качестве индикатора ошибки.

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

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

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

Ответ 6

Заводы - это слой абстракции, и, как и все абстракции, они имеют сложность. При столкновении с API на основе factory, определяющим, что означает factory для данного API, может быть сложным для потребителя API. С конструкторами открытость тривиальна.

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

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

Ответ 7

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

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

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

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