Перейти к содержанию

Конструктор

Создание конструкторов в 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, и благодаря ему легче работать с приватными переменными, у него есть несколько недостатков

  1. Он использует больше памяти, поскольку созданные объекты не хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода.
  2. Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый.
  3. Разрыв цепочки прототипов просто по причине забытого ключевого слова new идёт вразрез с духом языка.

Заключение

Хотя забытое ключевое слово new и может привести к багам, это точно не причина отказываться от использования прототипов. В конце концов, полезнее решить, какой из способов лучше совпадает с требованиями приложения: очень важно выбрать один из стилей создания объектов и после этого не изменять ему.