Всем привет! Сегодня мы поговорим с вами о конструкторах. Я расскажу вам о проблеме, которую можно легко решить при помощи конструкторов, а также покажу способ их создания. Для начала попробуем сформулировать проблему. Допустим, у нас есть некоторый объект — студент, который хранит его характеристики и какие-то необходимые действия. Так этот объект хранит имя студента в поле name. Обычно в программе мы работаем не с одним конкретным объектом, а с целой коллекцией однотипных объектов. И нам необходимо уметь создавать объекты того же типа. Так, мы захотим создать еще одного студента, но уже с другим именем Willy. Как мы видим, создание нового объекта такого же типа — достаточно громоздкая операция, нам приходится заново перечислять все поля, и, более того, заново писать реализацию методов. Таким образом, мы сталкиваемся с проблемой — проблемой дублирования кода при создании новых объектов. Решить эту проблему очень просто. Достаточно вынести процесс создания новых объектов в конструктор. Давайте попробуем создать такой конструктор своими руками. Пусть это будет обычная функция — назовем ее createStudent, которая на вход принимает в качестве параметра имя студента, а на выходе отдает нам уже готового студента — новый объект с заполненными полями и необходимыми методами. Таким образом, операция по созданию новых студентов становится очень простой и буквально занимает одну строчку. Мы можем легко создавать новых студентов. Данное решение достаточно простое, но обладает фатальным недостатком: каждый раз при вызове конструктора мы будем вновь создавать новую функцию, которая будет реализовывать необходимый нам метод sleep. Это не очень хорошо, и мы можем достаточно просто решить эту проблему — вынести этот метод в прототип. Для этого создадим новый объект studentProto, который будет являться прототипом для всех вновь создаваемых студентов, и перенесем туда наш метод sleep. После этого нам необходимо немножко усложнить код нашего конструктора и добавить туда вызов метода setPrototypeOf, который будет привязывать новых студентов к уже созданному нами прототипу. Благодаря таким изменениям мы можем создавать новых студентов, и каждый студент будет иметь доступ к методам, которые хранятся в прототипе для него. Мы можем не писать наш конструктор своими руками, а воспользоваться уже готовым механизмом, который предусмотрен в языке для создания конструкторов. Вообще, любая функция может быть конструктором, если мы вызовем ее при помощи специального оператора new. Даже наша функция createStudent может стать конструктором сама, если мы используем этот оператор. Но для того чтобы все работало как нужно, нам необходимо переписать ее реализацию и оставить в ней всего лишь одну строчку, а именно: передаваемый аргумент, который будет хранить имя вновь создаваемого студента, присвоить в поле name у объекта, который хранится по ссылке this. А всю остальную работу за нас сделает интерпретатор. Вначале, перед тем как исполнить код нашего конструктора, он создаст новый объект и присвоит его в переменную this. Далее мы заполним этот объект полями, следует та часть кода, которую мы пишем сами, и в конце интерпретатор за нас вернет объект, хранящийся по ссылке this. Можно сказать, что при вызове функции как конструктора с оператором new this внутри этой функции при исполнении будет указывать на вновь создаваемый объект. Итак, мы переписали наш конструктор createStudent, оставив в нем всего лишь одну строчку, и теперь вызываем его при помощи оператора new. Такой код уже будет работать, но читается он не очень хорошо. Как будто бы мы говорим, что «Billy — это новый создать студент». Давайте сделаем название функции чуть более семантичной и короткой: выкинем префикс create и оставим Student. Таким образом переименуем нашу функцию просто в функцию Student. И тогда наш код будет читаться гораздо лучше. Он будет читаться, как «Billy — это новый студент вот с таким именем». Так как функции конструктора — это обычные функции, сообщество разработчиков договорилось следовать некоему соглашению в их именовании, чтобы отличать вызов обычной функции от вызова функции, когда мы предполагаем ее использовать в качестве конструктора, а именно: они предлагают именовать функции конструктора с заглавной буквы. Чтобы следовать этому соглашению, нам необходимо еще раз поменять имя нашего конструктора, а именно записать слово Student с заглавной буквы. Почему это важно? Что произойдет, если мы попытаемся вызвать случайно, например, нашу функцию-конструктор как обычную функцию без оператора new? В этом случае переданное значение имени студента не будет записано в новый объект студента, а будет записано в глобальный объект в поле name, так как, вызывая функцию, this по умолчанию будет ссылаться на глобальный объект. Мы можем следовать соглашению и дополнительно включить строгий режим интерпретации, который защитит нас от такого поведения. В этом случае this будет иметь значение undefined, и мы не сможем присвоить в него никакие поля. Давайте попробуем вмешаться в работу конструктора, в работу интерпретатора и как-то поменять поведение конструктора. Например, мы захотим сами возвращать какой-то сконструированный объект. Можем ли мы это сделать? В данном случае интерпретатор нам полностью доверяет и вернет тот объект, который мы возвращаем при помощи оператора return. Но если мы попытаемся вернуть из конструктора какое-то примитивное значение — число, строку или null, в этом случае интерпретатор просто проигнорирует эту строку и будет работать как раньше: он будет возвращать вновь создаваемый объект. В данном видео мы с вами познакомились с конструкторами, со способом их создания и решили проблему дублирования кода при создании новых объектов.