W3docs

Оператор new в JavaScript

Узнайте, как конструкторы и оператор new создают объекты в JavaScript: пошаговая работа new, правило возвращаемого значения, new.target и связь с классами.

Введение в конструкторы и оператор new

В JavaScript конструкторы — это функции, предназначенные для инициализации вновь созданных объектов. Они играют ключевую роль в объектно-ориентированном программировании, позволяя разработчикам определять свойства и поведение, которые должны иметь объекты определённого класса. Оператор new используется для создания экземпляра объекта на основе функции-конструктора: он создаёт новое окружение объекта на основе указанного прототипа и запускает конструктор для инициализации нового объекта.

Как работают конструкторы

Функция-конструктор в JavaScript выглядит как обычная функция, однако по соглашению её имя начинается с заглавной буквы, чтобы отличить её от обычных функций. Когда оператор new вызывает функцию-конструктор, за кулисами происходят четыре вещи, примерно эквивалентные следующему псевдокоду:

function User(name) {
  // this = {};  (1) an empty object is created and assigned to this
  // this.__proto__ = User.prototype;  (2) prototype is linked

  this.name = name;  // (3) the constructor body runs, adding properties to this

  // return this;  (4) this is returned automatically
}
  1. Создаётся новый пустой object и присваивается this.
  2. Устанавливается связь с прототипом: внутреннее свойство [[Prototype]] нового объекта указывает на свойство prototype конструктора, поэтому объект наследует свойства и методы, определённые там.
  3. Выполняется тело конструктора: функция выполняется с переданными аргументами, а this ссылается на только что созданный объект, поэтому присваивания вида this.name = name добавляют к нему свойства.
  4. Объект возвращается: this возвращается автоматически, если только конструктор явно не возвращает другой object (см. Правило возвращаемого значения ниже).
Информация

В современном JavaScript для определения конструкторов и методов более интуитивно используется синтаксис класса. Он обеспечивает более понятный, классовый подход, аналогичный другим языкам программирования.

Пример: базовая функция-конструктор

javascript— editable

Пояснение: В этом примере User — это функция-конструктор, которая инициализирует name, age и метод greet на вновь созданных объектах. Выражение new User('John', 30) создаёт новый экземпляр User с именем «John» и возрастом 30. Внутри greet this ссылается на объект, для которого был вызван метод.

Правило возвращаемого значения

Конструкторы обычно не используют return — новый объект (this) возвращается автоматически. Однако return внутри конструктора имеет особое, легко упускаемое из виду поведение:

  • Если после return указан object, этот object возвращается вместо this.
  • Если после return указан примитив (string, число, boolean, undefined и т. д.), он игнорируется, и this возвращается как обычно.
javascript— editable

Пояснение: WithObject возвращает обычный object, поэтому он полностью заменяет экземпляр. WithPrimitive возвращает string, которая игнорируется, поэтому возвращается исходный thisname: 'Alice'). На практике это редко используется намеренно, но объясняет неожиданные результаты, когда конструктор случайно возвращает значение.

Определение вызова через new с помощью new.target

Внутри любой функции new.target равно undefined, когда функция вызвана обычным образом, и равно самой функции, когда она вызвана с new. Это позволяет конструктору определить, как именно он был вызван, — что полезно для принудительного использования (или молчаливого допущения) ключевого слова new.

javascript— editable

Пояснение: Поскольку Modal проверяет new.target, вызов Modal('Without new') без new прозрачно перенаправляется к new Modal(...), так что b по-прежнему является настоящим экземпляром Modal. (Заметьте: многие команды предпочитают не делать этого и вместо этого позволяют ошибочному отсутствию new явно завершаться ошибкой.)

Когда стоит использовать конструктор?

Используйте функцию-конструктор (или класс), когда нужно создать множество объектов одной формы — несколько пользователей, автомобилей, виджетов DOM и т. д. Конструктор централизует логику настройки, чтобы каждый экземпляр создавался одинаково и разделял методы через прототип.

Если нужен только один объект, проще воспользоваться литералом объекта { ... }. Для одноразового объекта, которому всё же полезно тело конструктора, можно использовать анонимный конструктор:

javascript— editable

Пояснение: Анонимная function запускается один раз как конструктор и нигде не сохраняется, поэтому не может быть использована повторно — это просто способ инкапсулировать сложную одноразовую настройку.

Функции-конструкторы и классы

В современном коде обычно предпочитают синтаксис class, который по сути является синтаксическим сахаром над функциями-конструкторами и прототипами. Два приведённых ниже фрагмента эквивалентны:

javascript— editable

Классы имеют реальные преимущества перед функциями-конструкторами: методы по умолчанию не являются перечисляемыми, тело выполняется в строгом режиме, вызов класса без new вызывает ошибку, а extends/super делают наследование значительно чище. Понимание функций-конструкторов по-прежнему важно, поскольку классы построены на том же механизме прототипов, и вы всё равно будете встречать их в старом коде.

Использование конструкторов для сложных объектов

Конструкторы можно использовать для настройки более сложных взаимодействий между объектами, включая методы, работающие с другими свойствами этих объектов.

Пример: конструктор с методами

javascript— editable

Пояснение: Конструктор Car настраивает каждый объект-автомобиль с конкретными свойствами и методом, отображающим информацию о нём.

Пример: методы прототипа

javascript— editable

Пояснение: Добавляя introduce в прототип Employee, все экземпляры разделяют один и тот же метод, что эффективнее по памяти, чем определение метода непосредственно в конструкторе.

Внимание

Рекомендуется использовать классы ES6 для определения объектов и конструкторов ради более чистого и читаемого кода.

Лучшие практики работы с конструкторами

При работе с конструкторами в JavaScript соблюдение определённых лучших практик может значительно улучшить читаемость, эффективность и масштабируемость кода. Ниже приведены подробные примеры и пояснения к каждой из них.

1. Соглашение об именовании

Лучшая практика: Всегда начинайте имена конструкторов с заглавной буквы, чтобы отличить их от обычных функций. Это общепринятое соглашение в JavaScript и многих других языках программирования, которое помогает разработчикам быстро идентифицировать функции-конструкторы.

Пример:

javascript— editable

Пояснение: Функция-конструктор Laptop начинается с заглавной буквы, что указывает на её предназначение для использования с оператором new при создании новых объектов.

2. Разделение логики

Лучшая практика: Для методов, которым не нужен доступ к данным конкретного экземпляра, определяйте их в прототипе конструктора, а не внутри него самого. Такой подход экономит память, поскольку все экземпляры используют один и тот же метод, вместо того чтобы каждый экземпляр создавал новую функцию в памяти.

Пример:

javascript— editable

Пояснение: Метод describe добавлен в прототип Book, то есть все экземпляры Book разделяют один и тот же метод describe. Это эффективнее, чем если бы describe был определён внутри конструктора — в этом случае для каждого экземпляра книги создавалась бы новая функция.

3. Возвращаемые значения

Лучшая практика: Избегайте возврата значений из конструкторов. JavaScript-конструкторы автоматически возвращают новый экземпляр объекта, если только явно не возвращается другой object. Возврат значений, не являющихся object (например, string или числа), не окажет никакого эффекта, и новый экземпляр всё равно будет возвращён.

Пример:

javascript— editable

Пояснение: Несмотря на попытку вернуть string из конструктора Player, JavaScript игнорирует это возвращаемое значение, поскольку оно не является object. Новый экземпляр Player возвращается, как и ожидается.

Заключение

Понимание конструкторов и оператора new в JavaScript необходимо для эффективного объектно-ориентированного программирования. Следуя изложенным здесь соглашениям и лучшим практикам, разработчики могут создавать организованный, эффективный и масштабируемый код. Конструкторы предоставляют мощный механизм для инициализации новых объектов и определения их поведения в структурированном и понятном виде.

Чтобы углубиться в тему, изучите, как конструкторы делятся поведением через прототипное наследование, как синтаксис class строится на этих идеях, и как функция сама является объектом со своими свойствами.

Практика

Практика
Что происходит, когда функция вызывается с 'new' в JavaScript?
Что происходит, когда функция вызывается с 'new' в JavaScript?
Was this page helpful?