Создание конструкторов в JavaScript также отличается от большинства других языков. Любая функция, вызванная с использованием ключевого слова new, будет конструктором.
Внутри конструктора (вызываемой функции) this будет указывать на новосозданный Object. Прототипом этого нового объекта будет prototype функции, которая была вызвана в качестве коструктора.
Если вызываемая функция не имеет явного возврата посредством return, то вернётся this — этот новый объект.
function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo();
В этом примере Foo вызывается в виде конструктора, следовательно прототип созданного объекта будет привязан к Foo.prototype.
В случае, когда функция в явном виде возвращает некое значение используя return, то в результате выполнения конструктора мы получим именно его, но только если возвращаемое значение представляет собой Object.
function Bar() {
return 2;
}
new Bar(); // новый объект
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // возвращённый объект
Если же опустить ключевое слово new, то функция не будет возвращать никаких объектов.
function Foo() {
this.bla = 1; // устанавливается глобальному объекту
}
Foo(); // undefined
Этот пример в некоторых случаях всё-таки может сработать: это связано с поведением this в JavaScript — он будет восприниматься парсером как глобальный объект.
Если хотите избавится от необходимости использования new, напишите конструктор, возвращающий значение посредством return.
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
В обоих случаях при вызове Bar мы получим один и тот же результат — новый объект со свойством method (спасибо замыканию за это).
Также следует заметить, что вызов new Bar() никак не связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но Bar никогда не возвращает этот новый объект.
В предыдущем примере нет функциональных отличий между вызовом конструктора с оператором new или без него.
Часто рекомендуют не использовать new, поскольку если вы его забудете, это может привести к ошибкам.
Чтобы создать новый объект, лучше использовать фабрику и создать новый объект внутри этой фабрики.
function Foo() {
var obj = {};
obj.value = 'blub';
var private = 2;
obj.someMethod = function(value) {
this.value = value;
}
obj.getPrivate = function() {
return private;
}
return obj;
}
Хотя данный пример и сработает, если вы забыли ключевое слово new и благодаря ему легче работать с приватными переменными, у него есть несколько недостатков
- Он использует больше памяти, поскольку созданные объекты не хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода.
- Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый.
- Разрыв цепочки прототипов просто по причине забытого ключевого слова
newидёт в разрез с духом языка.
Хотя забытое ключевое слово new и может привести к багам, это точно не причина отказываться от использования прототипов. В конце концов, полезнее решить какой из способов лучше совпадает с требованиями приложения: очень важно выбрать один из стилей создания объектов и после этого не изменять ему.