Функция-конструктор для создания объекта в JavaScript

В этом руководстве вы узнаете о функции-конструкторе в 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, чтобы сделать синтаксис более гибким.

Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript