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