Может кто-нибудь прояснить разницу между функцией конструктора и фабричной функцией в Javascript.
Когда использовать один вместо другого?
источник
Может кто-нибудь прояснить разницу между функцией конструктора и фабричной функцией в Javascript.
Когда использовать один вместо другого?
Основное отличие состоит в том, что функция конструктора используется с 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;
}
Конечно, вы можете сделать фабричные функции намного более сложными, чем этот простой пример.
Одним из преимуществ фабричных функций является то, что возвращаемый объект может иметь несколько различных типов в зависимости от какого-либо параметра.
someMethod
объекты, возвращаемые фабрикой, и вот где она становится немного туманной. Внутри фабричной функции, если это просто такvar obj = { ... , someMethod: function() {}, ... }
, это приведет к тому, что каждый возвращаемый объект будет содержать другую копиюsomeMethod
, чего мы можем не хотеть. Вот где использованиеnew
иprototype
внутри фабричной функции поможет.new
функцию конструктора; Я подумал, что именно здесь, возможно, понадобится посмотреть, как заменить конструкторы примером фабричных функций, и именно здесь я подумал, что в примерах требуется согласованность. В любом случае, ответ достаточно информативен. Это была просто точка, которую я хотел поднять, а не то, что я каким-то образом теряю качество ответа.new
внутренне или использоватьObject.create()
для создания объекта с конкретным прототипом.Преимущества использования конструкторов
Большинство книг учат вас использовать конструкторы и
new
this
относится к новому объектуНекоторым людям нравится, как
var myFoo = new Foo();
читает.Недостатки
Детали создания экземпляров просочились в вызывающий 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 - см. преимущества).источник
new
нарушает принцип открытого / закрытого. См. Medium.com/javascript-scene/… для гораздо большего обсуждения, чем позволяют эти комментарии.new
ключевого слова, я не верю, чтоnew
ключевое слово действительно обеспечивает дополнительную читаемость. IMO, кажется глупым перепрыгивать через обручи, чтобы позволить звонящим набирать больше.Конструктор возвращает экземпляр класса, к которому вы обращаетесь. Заводская функция может вернуть все что угодно. Вы бы использовали фабричную функцию, когда вам нужно вернуть произвольные значения или когда у класса большой процесс установки.
источник
Пример функции конструктора
new
создает прототип объектаUser.prototype
и вызываетUser
с созданным объектом в качествеthis
значения.new
обрабатывает выражение аргумента для своего операнда как необязательное:приведет
new
к вызовуUser
без аргументов.new
возвращает созданный им объект, если только конструктор не возвращает значение объекта , которое возвращается взамен. Это крайний случай, который по большей части можно игнорировать.Плюсы и минусы
Объекты, созданные функциями конструктора, наследуют свойства от свойства конструктора
prototype
и возвращают значение true, используяinstanceOf
оператор функции конструктора.Вышеуказанное поведение может завершиться ошибкой, если вы динамически измените значение
prototype
свойства конструктора после того, как уже использовали конструктор. Это редко , и его нельзя изменить, если конструктор был создан с помощьюclass
ключевого слова.Функции конструктора могут быть расширены с помощью
extends
ключевого слова.Функции конструктора не могут возвращаться
null
как значение ошибки. Поскольку это не тип данных объекта, он игнорируетсяnew
.Пример фабричной функции
Здесь заводская функция вызывается без
new
. Функция полностью отвечает за прямое или косвенное использование своих аргументов и типа возвращаемого объекта. В этом примере он возвращает простой [объект объекта] с некоторыми свойствами, установленными из аргументов.Плюсы и минусы
Легко скрывает сложности реализации создания объекта от вызывающей стороны. Это особенно полезно для функций собственного кода в браузере.
Функция фабрики не всегда должна возвращать объекты одного и того же типа и даже может возвращаться
null
как индикатор ошибки.В простых случаях фабричные функции могут быть простыми по структуре и значению.
Возвращаемые объекты обычно не наследуются от
prototype
свойства фабричной функции и возвращаютсяfalse
изinstanceOf factoryFunction
.Фабричную функцию нельзя безопасно расширять с помощью
extends
ключевого слова, поскольку расширенные объекты наследуются отprototype
свойства фабричных функций, а не отprototype
свойства конструктора, используемого фабричной функцией.источник
Фабрики "всегда" лучше. При использовании объектно-ориентированных языков
Реализации (фактические объекты, созданные с помощью new) не предоставляются фабричному пользователю / потребителю. Это означает, что разработчик фабрики может расширять и создавать новые реализации до тех пор, пока он / она не нарушает контракт ... и это позволяет потребителю фабрики просто получать выгоду от нового API без необходимости изменять свой код ... если они использовали новую, и приходит «новая» реализация, тогда они должны переходить и менять каждую строку, которая использует «новую», чтобы использовать «новую» реализацию ... с фабрикой их код не меняется ...
Фабрики - лучше, чем все остальное - основа пружины полностью построена вокруг этой идеи.
источник
Фабрики - это слой абстракций, и, как и все абстракции, они имеют сложность. Когда вы сталкиваетесь с API на основе фабрики, выяснить, что такое фабрика для данного API, может быть сложно для потребителя API. С конструкторами открываемость тривиальна.
При выборе между ctors и фабриками вы должны решить, оправдывает ли сложность выгода.
Стоит отметить, что конструкторы Javascript могут быть произвольными фабриками, возвращая что-то отличное от this или undefined. Таким образом, в js вы можете получить лучшее из обоих миров - обнаруживаемый API и пул / кеширование объектов.
источник
new
, изменения поведенияthis
, изменения возвращаемого значения, подключения прототипа ссылки, включенияinstanceof
(которое лежит и не должно использоваться для этой цели). Якобы, все это "особенности". На практике они ухудшают качество вашего кода.Что касается различий, Эрик Эллиот объяснил очень хорошо,
Но для второго вопроса:
Если вы исходите из объектно-ориентированного фона, функция Constructor выглядит более естественной для вас. Таким образом, вы не должны забывать использовать
new
ключевое слово.источник