Відповіді:
Основна відмінність полягає в тому, що функція конструктора використовується з 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() }
). EDIT: З 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
, як очікувалося. Якщо ви зберігаєте прототипи на батьківському об'єкті, може бути чудовим способом динамічно заміняти функціональність та ввімкнути дуже гнучкий поліморфізм для обґрунтування об'єкта.
Немає двозначності щодо капіталізації чи ні. Не варто. Інструменти для ворсу будуть скаржитися, і тоді ви будете спокушатися спробувати використовувати 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
і повертають істину, використовуючи instanceOf
оператор функції конструктора.
Вищеописані способи поведінки можуть бути невдалими, якщо ви динамічно змінюєте значення властивості конструктора prototype
після того, як ви вже використали конструктор. Це рідко , і його неможливо змінити, якщо конструктор був створений за допомогою class
ключового слова.
Функції конструктора можна розширити за допомогою extends
ключового слова.
Функції конструктора не можуть повернутися null
як значення помилки. Оскільки це не тип об'єктних даних, він ігнорується new
.
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Тут заводська функція викликається без new
. Функція повністю відповідає за пряме або опосередковане використання, якщо її аргументи та тип об'єкта, який вона повертає. У цьому прикладі він повертає простий [Object object] з деякими властивостями, встановленими з аргументів.
Легко приховує складності реалізації об'єкта створення від абонента. Це особливо корисно для функцій нативного коду в браузері.
Фабрична функція не завжди повинна повертати об'єкти одного типу і навіть може повертатися null
як індикатор помилок.
У простих випадках фабричні функції можуть бути простими за структурою та значенням.
Об'єкти, що повертаються, як правило, не успадковують prototype
властивості заводської функції , а повертаються false
з instanceOf factoryFunction
.
Фабричну функцію не можна безпечно розширити за допомогою extends
ключового слова, оскільки розширені об'єкти успадкують від prototype
властивості заводських функцій замість prototype
властивості конструктора, використовуваного заводською функцією.
Заводи "завжди" кращі. Тоді при використанні об'єктно-орієнтованих мов
Реалізації (фактичні об'єкти, створені за допомогою нового) не піддаються фабричному користувачеві / споживачеві. Це означає, що розробник фабрики може розширювати та створювати нові реалізації, поки він / вона не порушить договір ... і це дозволяє заводському споживачеві просто отримати вигоду від нового API, не змінюючи їх код ... якщо вони використовували нову, а "нова" реалізація приходить разом, вони повинні перейти і змінити кожен рядок, який використовує "новий", щоб використовувати "нову" реалізацію ... з фабрикою їх код не змінюється ...
Фабрики - кращі за все інше - весняні рамки повністю побудовані навколо цієї ідеї.
Фабрики - це шар абстракції, і, як і всі абстракції, вони мають складну вартість. Зустрічаючи API на заводі, з'ясування того, що є фабрикою для даного API, може бути складним для споживача API. Відкриття конструкцій тривіально.
Вирішуючи питання між автотранспортом і фабриками, ви повинні вирішити, чи складність виправдана вигодою.
Варто зауважити, що конструктори Javascript можуть бути довільними фабриками, повертаючи щось інше, ніж це або невизначене. Таким чином, у js ви можете отримати найкраще з обох світів - відкритий API і об'єднання об'єктів / кешування.
new
, Змінюючи поведінку this
, Змінюючи повернене значення, Підключення прототипу ref, Увімкнення instanceof
(що лежить і не повинно використовуватися для цієї мети). Нібито, все це - "риси". На практиці вони шкодять якості вашого коду.
Для відмінностей Ерік Еліотт дуже добре уточнив,
Але для другого питання:
Коли використовувати один замість іншого?
Якщо ви надходите з об'єктно-орієнтованого фону, функція Constructor виглядає для вас більш природно. таким чином, ви не повинні забути використовувати new
ключове слово.