Функция конструктора 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
ключевое слово.