В этом руководстве вы узнаете о функции-конструкторе в JavaScript и как использовать ключевое слово new
для создания объекта.
В руководстве по объектам JavaScript вы узнали, как использовать синтаксис литерала для создания нового объекта.
Например, в следующем примере создается новый объект person с двумя свойствами firstName
и lastName
:
let person = { firstName: 'John', lastName: 'Doe' };
На практике вам часто нужно создать много подобных объектов, таких как объект person
.
Для этого вы можете использовать функцию конструктор для определения пользовательского типа и оператор new
для создания нескольких объектов из этого типа.
С технической точки зрения функция-конструктор — это обычная функция со следующим соглашением:
- Имя функции-конструктора начинается с заглавной буквы, например,
Person
,Document
и т. д. - Функция-конструктор должна вызываться только с оператором
new
.
Обратите внимание, что ES6 вводит ключевое слово class
, которое позволяет вам определять пользовательский тип. А классы — это просто синтаксический сахар над функциями-конструкторами с некоторыми улучшениями.
В следующем примере определяется функция-конструктор с именем Person
:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; }
В этом примере Person
такая же, как и обычная функция, за исключением того, что ее имя начинается с заглавной буквы P
Чтобы создать новый экземпляр Person
, вы используете new
оператор:
let person = new Person('John','Doe');
По сути, new
оператор делает следующее:
- Создает новый пустой объект и назначает его переменной
this
. - Назначает аргументы
'John'
и'Doe'
свойствамfirstName
иlastName
объекта. - Возвращает
this
значение.
Это функционально эквивалентно следующему:
function Person(firstName, lastName) { // this = {}; // add properties to this this.firstName = firstName; this.lastName = lastName; // return this; }
Следующее утверждение:
let person = new Person('John','Doe');
… возвращает тот же результат, что и следующий оператор:
let person = { firstName: 'John', lastName: 'Doe' };
Однако функция-конструктор Person
позволяет создавать несколько похожих объектов. Например:
let person1 = new Person('Jane','Doe') let person2 = new Person('James','Smith')
Добавление методов
Объект может иметь методы, которые манипулируют его данными. Чтобы добавить метод к объекту, созданному с помощью функции-конструктора, вы можете использовать ключевое слово this
. Например:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = function() { return this.firstName + " " + this.lastName; }; }
Теперь вы можете создать новый объект Person
и вызвать метод getFullName()
:
let person = new Person("John", "Doe"); console.log(person.getFullName());
Выход:
John Doe
Проблема с функцией конструктора заключается в том, что когда вы создаете несколько экземпляров Person
, this.getFullName()
дублируется в каждом экземпляре, что неэффективно с точки зрения использования памяти.
Чтобы решить эту проблему, вы можете использовать прототип, чтобы все экземпляры пользовательского типа могли использовать одни и те же методы.
Возврат из функций-конструкторов
Как правило, функция-конструктор неявно возвращает this
значение для вновь созданного объекта. Но если у него есть оператор return
, то вот правила:
- Если
return
вызывается с объектом, функция-конструктор возвращает этот объект вместоthis
. - Если
return
вызывается со значением, отличным от объекта, он игнорируется.
Вызов без new
ключевого слова
Технически вы можете вызывать функцию-конструктор как обычную функцию, не используя ключевое слово new
, например:
let person = Person('John','Doe');
В этом случае Person
просто выполняется как обычная функция. Следовательно, this
внутри функции Person
привязывается не к переменной person
, а к глобальному объекту.
Если вы попытаетесь получить доступ к свойству firstName
или lastName
, вы получите сообщение об ошибке:
console.log(person.firstName);
Ошибка:
TypeError: Cannot read property 'firstName' of undefined
Точно так же вы не можете получить доступ к getFullName()
, поскольку он привязан к глобальному объекту.
person.getFullName();
Ошибка:
TypeError: Cannot read property 'getFullName' of undefined
Чтобы предотвратить вызов функции конструктора без ключевого слова new
, в ES6 было введено свойство new.target
.
Если функция-конструктор вызывается с ключевым словом new
, new.target
возвращает ссылку на функцию. В противном случае он возвращает undefined
.
Следующее добавляет оператор внутри функции Person
, чтобы показать new.target
на консоли:
function Person(firstName, lastName) { console.log(new.target); this.firstName = firstName; this.lastName = lastName; this.getFullName = function() { return this.firstName + " " + this.lastName; }; }
Следующее возвращает undefined
, потому что функция-конструктор Person
вызывается как обычная функция:
let person = Person("John", "Doe");
Выход:
undefined
Однако следующий код возвращает ссылку на функцию Person
, поскольку она вызывается с ключевым словом new
:
let person = new Person("John", "Doe");
Выход:
[Function: Person]
Используя new.target
, вы можете заставить вызывающие функции конструктора использовать new
ключевое слово. В противном случае вам может выдать такую ошибку:
function Person(firstName, lastName) { if (!new.target) { throw Error("Cannot be called without the new keyword"); } this.firstName = firstName; this.lastName = lastName; }
Кроме того, вы можете сделать синтаксис более гибким, создав новый объект Person
, если пользователи функции-конструктора не используют ключевое слово new
:
function Person(firstName, lastName) { if (!new.target) { return new Person(firstName, lastName); } this.firstName = firstName; this.lastName = lastName; } let person = Person("John", "Doe"); console.log(person.firstName);
Этот шаблон часто используется в библиотеках и фреймворках JavaScript, чтобы сделать синтаксис более гибким.