Обычный синтаксис {...}
позволяет нам создать один объект. Но часто нам нужно создать множество похожих объектов, например, нескольких пользователей или пунктов меню и так далее.
Это можно сделать с помощью функций-конструкторов и оператора "new"
.
Функции конструктора технически являются обычными функциями. Однако есть два соглашения:
Их имена начинаются с заглавной буквы.
Их следует выполнять только с "new"
оператором.
Например:
функция Пользователь(имя) { это.имя = имя; this.isAdmin = ложь; } пусть пользователь = новый пользователь («Джек»); оповещение(имя_пользователя); // Джек оповещение (user.isAdmin); // ЛОЖЬ
Когда функция выполняется с помощью new
, она выполняет следующие шаги:
Создается новый пустой объект и присваивается this
.
Тело функции выполняется. Обычно он модифицирует this
, добавляет к нему новые свойства.
Значение this
возвращается.
Другими словами, new User(...)
делает что-то вроде:
функция Пользователь(имя) { // это = {}; (косвенно) // добавляем сюда свойства это.имя = имя; this.isAdmin = ложь; // возвращаем это; (косвенно) }
Итак, let user = new User("Jack")
дает тот же результат, что и:
пусть пользователь = { имя: "Джек", isAdmin: ложь };
Теперь, если мы хотим создать других пользователей, мы можем вызвать new User("Ann")
, new User("Alice")
и так далее. Гораздо короче, чем каждый раз использовать литералы, и к тому же легко читается.
Это основная цель конструкторов — реализовать код создания многократно используемых объектов.
Еще раз отметим — технически в качестве конструктора можно использовать любую функцию (кроме стрелочных функций, так как у них this
нет). Его можно запустить с помощью new
, и он выполнит приведенный выше алгоритм. «Сначала заглавная буква» — это общепринятое соглашение, которое дает понять, что функция должна запускаться с помощью new
.
новая функция() { … }
Если у нас есть много строк кода, посвященных созданию одного сложного объекта, мы можем обернуть их в немедленно вызываемую функцию-конструктор, например:
// создаем функцию и сразу вызываем ее с помощью new пусть пользователь = новая функция() { this.name = "Джон"; this.isAdmin = ложь; // ...другой код для создания пользователя // возможно, сложная логика и утверждения // локальные переменные и т.д. };
Этот конструктор нельзя вызвать повторно, поскольку он нигде не сохраняется, а просто создается и вызывается. Таким образом, этот трюк направлен на инкапсуляцию кода, создающего один объект, без повторного использования в будущем.
Расширенные возможности
Синтаксис из этого раздела используется редко, пропустите его, если не хотите знать все.
Внутри функции мы можем проверить, была ли она вызвана с new
или без него, используя специальное свойство new.target
.
Она не определена для обычных вызовов и равна функции, если она вызывается с помощью new
:
функция Пользователь() { оповещение (новая.цель); } // без "нового": Пользователь(); // неопределенный // с "новым": новый пользователь(); // функция Пользователь { ... }
Это можно использовать внутри функции, чтобы узнать, была ли она вызвана с new
«в режиме конструктора» или без него «в обычном режиме».
Мы также можем делать то же самое как new
, так и обычные вызовы, например:
функция Пользователь(имя) { if (!new.target) { // если вы запустите меня без new вернуть нового пользователя (имя); // ...я добавлю для вас новое } это.имя = имя; } пусть Джон = Пользователь("Джон"); // перенаправляет вызов новому пользователю оповещение(john.name); // Джон
Этот подход иногда используется в библиотеках, чтобы сделать синтаксис более гибким. Чтобы люди могли вызывать функцию с new
или без него, и она все равно работала.
Однако, вероятно, не стоит использовать его везде, поскольку отсутствие new
делает происходящее менее очевидным. При использовании new
мы все знаем, что создается новый объект.
Обычно конструкторы не имеют оператора return
. Их задача — записать в this
файл всё необходимое, и он автоматически становится результатом.
Но если есть оператор return
, то правило простое:
Если return
вызывается с объектом, то вместо this
возвращается объект.
Если return
вызывается с примитивом, он игнорируется.
Другими словами, return
с объектом возвращает этот объект, во всех остальных случаях возвращается this
.
Например, здесь return
переопределяет this
, возвращая объект:
функция BigUser() { this.name = "Джон"; return {имя: "Годзилла"}; // <-- возвращает этот объект } предупреждение (новый BigUser().name); // Годзилла, получил этот объект
А вот пример с пустым return
(или мы могли бы разместить после него примитив, не имеет значения):
функция SmallUser() { this.name = "Джон"; возвращаться; // <-- возвращает это } предупреждение (новый SmallUser().name); // Джон
Обычно конструкторы не имеют оператора return
. Здесь мы упоминаем особое поведение при возврате объектов главным образом ради полноты картины.
Опускание круглых скобок
Кстати, круглые скобки после new
можно опустить:
пусть пользователь = новый пользователь; // <-- без круглых скобок // то же, что пусть пользователь = новый пользователь();
Пропуск круглых скобок здесь не считается «хорошим стилем», но синтаксис разрешен спецификацией.
Использование функций конструктора для создания объектов обеспечивает большую гибкость. Функция-конструктор может иметь параметры, которые определяют, как создать объект и что в него поместить.
Конечно, мы можем добавить this
не только свойства, но и методы.
Например, new User(name)
ниже создает объект с заданным name
и sayHi
:
функция Пользователь(имя) { это.имя = имя; this.sayHi = function() { alert( "Меня зовут: " + this.name ); }; } пусть Джон = новый Пользователь("Джон"); Джон.sayHi(); // Меня зовут: Джон /* Джон = { имя: «Джон», SayHi: function() { ... } } */
Для создания сложных объектов существует более продвинутый синтаксис — классы, которые мы рассмотрим позже.
Функции-конструкторы или, кратко, конструкторы — это обычные функции, но общепринято называть их первыми с заглавной буквы.
Функции конструктора следует вызывать только с использованием new
. Такой вызов подразумевает создание пустого this
в начале и возврат заполненного в конце.
Мы можем использовать функции-конструкторы для создания нескольких похожих объектов.
JavaScript предоставляет функции-конструкторы для многих встроенных языковых объектов: например, Date
для дат, Set
для множеств и других, которые мы планируем изучить.
Объекты, мы вернемся!
В этой главе мы рассмотрим только основы объектов и конструкторов. Они необходимы для получения дополнительной информации о типах данных и функциях в следующих главах.
После того, как мы это узнаем, мы вернемся к объектам и подробно рассмотрим их в главах «Прототипы», «Наследование» и «Классы».
важность: 2
Можно ли создать функции A
и B
так, чтобы new A() == new B()
?
функция А() { ... } функция Б() { ... } пусть a = новый A(); пусть b = новый B(); Предупреждение (а == б); // истинный
Если да, то приведите пример их кода.
Да, это возможно.
Если функция возвращает объект, то new
возвращает его вместо this
.
Таким образом, они могут, например, возвращать один и тот же внешний объект obj
:
пусть объект = {}; функция А() {возвращение объекта; } функция B() {возвращение объекта; } предупреждение (новый A() == новый B()); // истинный
важность: 5
Создайте функцию-конструктор Calculator
, которая создает объекты тремя методами:
read()
запрашивает два значения и сохраняет их как свойства объекта с именами a
и b
соответственно.
sum()
возвращает сумму этих свойств.
mul()
возвращает произведение этих свойств.
Например:
пусть калькулятор = новый калькулятор(); калькулятор.читать(); alert( "Sum=" + калькулятор.сум() ); alert( "Mul=" + калькулятор.mul() );
Запустить демо-версию
Откройте песочницу с тестами.
функция Калькулятор() { this.read = функция() { this.a = +prompt('a?', 0); this.b = +prompt('b?', 0); }; this.sum = функция() { вернуть this.a + this.b; }; this.mul = функция() { верните this.a * this.b; }; } пусть калькулятор = новый калькулятор(); калькулятор.читать(); alert( "Sum=" + калькулятор.сум() ); alert( "Mul=" + калькулятор.mul() );
Откройте решение с тестами в песочнице.
важность: 5
Создайте функцию-конструктор Accumulator(startingValue)
.
Объект, который он создает, должен:
Сохраните «текущее значение» в свойстве value
. Начальное значение устанавливается в качестве аргумента конструктора startingValue
.
Метод read()
должен использовать prompt
для чтения нового числа и добавления его к value
.
Другими словами, свойство value
представляет собой сумму всех введенных пользователем значений с начальным значением startingValue
.
Вот демо-версия кода:
пусть аккумулятор = новый аккумулятор (1); // начальное значение 1 аккумулятор.читать(); // добавляет введенное пользователем значение аккумулятор.читать(); // добавляет введенное пользователем значение оповещение(аккумулятор.значение); // показывает сумму этих значений
Запустить демо-версию
Откройте песочницу с тестами.
функция Аккумулятор (начальное значение) { this.value = начальное значение; this.read = функция() { this.value += +prompt('Сколько добавить?', 0); }; } пусть аккумулятор = новый аккумулятор (1); аккумулятор.читать(); аккумулятор.читать(); оповещение(аккумулятор.значение);
Откройте решение с тестами в песочнице.