Конструктор
Когда начальные значения заданы раз и навсегда, каждый новый монстр начинает гоблином с 30 здоровья, и поля приходится менять уже после new. Удобнее сразу сказать, чего вы хотите: new Monster("Волк", 25). Для этого и нужен конструктор:
class Monster {
name: string;
health: number;
constructor(name: string, health: number) {
this.name = name;
this.health = health;
}
}
const wolf = new Monster("Волк", 25);
const troll = new Monster("Тролль", 80);
console.log(wolf);
console.log(troll);Программа выводит:
Monster { name: "Волк", health: 25 }
Monster { name: "Тролль", health: 80 }Конструктор — особая функция внутри класса с именем constructor. Вы никогда не вызываете её сами: её вызывает new при создании каждого объекта, а аргументы new Monster("Волк", 25) попадают в её параметры. У параметров есть типы, как у любой функции, поэтому TypeScript проверяет аргументы new: new Monster("Волк") даёт Expected 2 arguments, but got 1. (TS2554), а new Monster(25, "Волк") — это TS2345.
Что такое this? Внутри конструктора this — это новый объект, который сейчас создаётся. this.name = name; значит: «Запиши в поле name нового объекта значение параметра name». Слева и справа стоит одно и то же имя, это обычное дело, но это две разные вещи: this.name — поле, а просто name — параметр.
Вот что делает new Monster("Волк", 25):
- Создаёт новый пустой объект класса
Monster. - Задаёт те поля, у которых есть начальное значение.
- Запускает конструктор, в котором
this— это новый объект, чтобы конструктор заполнил остальное. - Отдаёт вам готовый объект.
У полей name и health сейчас нет начальных значений, и это нормально: TypeScript проверяет, что конструктор присваивает значение каждому из них. Поле может и сохранить начальное значение, и вообще не встречаться в конструкторе, как level: number = 1;.
Типичная ошибка: забыть this. в this.name = name;. Тогда name = name; просто присваивает параметр самому себе, а поле остаётся пустым. TypeScript это замечает:
class Monster {
name: string;
health: number;
constructor(name: string, health: number) {
name = name;
this.health = health;
}
}TypeScript сообщает:
main.ts(2,5): error TS2564: Property 'name' has no initializer and is not definitely assigned in the constructor.Заметка на будущее. В чужом коде на TypeScript вы встретите более короткую запись: constructor(public name: string) {} объявляет поле и сразу задаёт его. Это называется свойством-параметром (parameter property), и этот сайт его не разрешает: вы получите This syntax is not allowed when 'erasableSyntaxOnly' is enabled. (TS1294), то же сообщение, что и для enum в уроке «Объединения и сужение типов». Свойство-параметр — это не просто JavaScript с типами: TypeScript приходится генерировать для него дополнительный код, а инструменты, которые лишь убирают типы, так не умеют. Поэтому пишите поля и this.name = name;, как выше.
Задание
- 1
Дайте
Heroконструктор с двумя параметрами,name: stringиheroClass: HeroClass, который задаёт поляnameиheroClass. Уберите начальные значения у этих двух полей (уlevelостаётся1). Тогда TypeScript отметит строки сnew Hero(): передайте им аргументы,new Hero("Кира", "следопыт")иnew Hero("Бруно", "воин"), и удалите две строки, которые потом меняли Бруно. Программа выводит то же, что и раньше. - 2
К отряду присоединяется маг Лена. Создайте
lenaчерез конструктор и выведите её, обратившись к её полям, так:Лена, маг, уровень 1