Функции-конструкторы и оператор new
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Один объект удобно написать литералом. Но когда таких объектов нужно двадцать и все одной формы, копипаста надоедает — тогда в дело идут функция-конструктор и оператор new.
Литерал { } отлично описывает один объект. Проблема начинается, когда объектов много и они устроены одинаково: пользователь, ещё пользователь, ещё сорок пользователей. Каждый раз выписывать одни и те же поля — скучно и легко ошибиться.
Функция-конструктор — это чертёж. Обычная функция, внутри которой описано, какие поля будут у объекта, а оператор new — команда «изготовь по этому чертежу одну штуку».
Оператор new выглядит магией, но магии там ровно на четыре действия. Он всегда делает их в одном и том же порядке, для любой функции.
| Шаг | Что делает new |
|---|---|
| 1 | Создаёт новый пустой объект |
| 2 | Записывает его в this |
| 3 | Выполняет тело функции — оно дописывает в this поля |
| 4 | Возвращает получившийся объект |
Первый и четвёртый шаги в коде не видны — они происходят сами. Если дописать их руками, конструктор перестанет быть загадочным:
Конструктор — не особый сорт функции. Это обычная функция, которую позвали через new. Она же, вызванная без new, — просто функция.
Если аргументов нет, скобки разрешено опустить: new User и new User() сделают одно и то же. Так писать не стоит — скобки подсказывают читателю, что здесь вызов.
Внутри конструктора this — это и есть будущий объект, ещё пустой. Всё, что вы ему присвоите, окажется в результате. Присваивать можно не только данные: функция в свойстве — это метод, и при вызове obj.method() её собственный this тоже укажет на объект.
Обратите внимание: return в конструкторе обычно не пишут — объект вернётся сам. Но если написать, у языка есть на этот счёт отдельное правило.
Правило простое: return с объектом подменяет результат целиком, а return с примитивом (или без значения) просто игнорируется — вернётся this.
Конструкторы принято называть с большой буквы: User, Point, Counter. Движку всё равно — new сработает и с user. Это договорённость между людьми: заглавная буква говорит «меня зовут через new», и по одному имени в чужом коде видно, вызов это или изготовление объекта.
А вот чего договорённостями не обойти: стрелочная функция конструктором быть не может. У неё нет собственного this, и new с ней падает.
Внутри любой функции доступно специальное значение new.target. При обычном вызове оно равно undefined, при вызове через new — самой функции. Это единственный честный способ узнать изнутри, как вас позвали.
Чаще всего его используют для подстраховки: если new забыли, конструктор дописывает его сам. Приём известный, но злоупотреблять им не стоит: код, который работает и так и эдак, читать труднее. Иногда полезнее наоборот — бросить понятную ошибку.
new Имя()создаёт пустой объект, кладёт его вthis, выполняет тело функции и возвращает получившийся объект- Внутри конструктора
this— это будущий объект; функции в его свойствах становятся методами returnобычно не нужен: с объектом он подменяет результат, с примитивом игнорируется- Заглавная буква в имени — соглашение, а не правило языка; стрелочная функция конструктором быть не может
new.targetравен самой функции при вызове черезnewиundefinedбез него;class— та же механика в более короткой записи
1/3Функция-конструктор заканчивается строкой return 42. Что вернёт вызов через new?
2/3Чему равен new.target внутри функции, вызванной обычным способом, без new?
3/3Что означает заглавная буква в имени функции-конструктора?
Аккумулятор
Напишите функцию-конструктор Accumulator(startValue). Она создаёт объект со свойством value (в него попадает аргумент) и методом add(n), который прибавляет n к value.
Начальное значение кладём в this.value. Метод — обычная функция в свойстве объекта: её вызывают как acc.add(5), поэтому внутри неё this тоже указывает на наш объект.
Метод не обязан ничего возвращать — он меняет состояние объекта, а результат читают из acc.value.
Конструктор, которому всё равно
Допишите Point так, чтобы объект получался и с new, и без него. Само тело конструктора менять не нужно — хватит одной строки в начале.
Проверяем new.target: если он пустой, значит функцию позвали обычным вызовом — тогда зовём сами себя правильно и возвращаем результат.
Возврат объекта из конструктора здесь как раз уместен: return с объектом подменяет результат, поэтому и вызов через new Point(...) отработает без сюрпризов.