Может кто-то уточнить разницу между функцией конструктора и функцией factory в Javascript.
Когда использовать один вместо другого?
Может кто-то уточнить разницу между функцией конструктора и функцией factory в Javascript.
Когда использовать один вместо другого?
Основное отличие состоит в том, что функция-конструктор используется с ключевым словом 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 является то, что возвращаемый объект может иметь несколько разных типов в зависимости от некоторого параметра.
В большинстве книг вам предлагается использовать конструкторы и 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 - см. преимущества).
Конструктор возвращает экземпляр класса, на который вы его вызываете. Функция factory может возвращать что угодно. Вы должны использовать функцию factory, когда вам нужно возвращать произвольные значения или когда у класса есть большой процесс настройки.
Заводы "всегда" лучше. При использовании объектно ориентированных языков, тогда
Реализации (фактические объекты, созданные с помощью нового) не отображаются пользователю/потребителю factory. Это означает, что разработчик factory может расширять и создавать новые реализации до тех пор, пока он/она не нарушит контракт... и он позволяет потребителю factory просто извлекать выгоду из нового API без необходимости изменять их код... если они используют новые, и возникает "новая" реализация, то они должны идти и менять каждую строку, которая использует "новую" для использования "новой" реализации... с factory их код не изменить...
Заводы - лучше, чем все остальное - инфраструктура spring полностью построена вокруг этой идеи.
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
конструктора, используемого функцией фабрики.
Заводы - это слой абстракции, и, как и все абстракции, они имеют сложность. При столкновении с API на основе factory, определяющим, что означает factory для данного API, может быть сложным для потребителя API. С конструкторами открытость тривиальна.
При принятии решения между ctors и фабриками вам нужно решить, оправдана ли эта сложность выгодой.
Стоит отметить, что конструкторы Javascript могут быть произвольными фабриками, возвращая что-то другое, чем это или undefined. Таким образом, в js вы можете получить лучшее из обоих миров - обнаруживаемый API и пул объектов/кэширование.
Что касается различий, Эрик Эллиот объяснил очень хорошо,
Но для второго вопроса:
Когда использовать один вместо другого?
Если вы исходите из объектно-ориентированного фона, функция Constructor выглядит более естественной для вас. Таким образом, вы не должны забывать использовать new
ключевое слово.