Конструктор¶
Создание конструкторов в 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
и может привести к багам, это точно не причина отказываться от использования прототипов. В конце концов, полезнее решить, какой из способов лучше совпадает с требованиями приложения: очень важно выбрать один из стилей создания объектов и после этого не изменять ему.