Всё сводится к пропуску одного слова. Ты пишешь const shirt = Product('Футболка', 1500) вместо const shirt = new Product(...). Отсутствие new превращает вызов конструктора в обычный вызов функции — и все правила из главы 2 начинают действовать: this становится глобальным объектом или undefined в строгом режиме. Функция, задуманная для создания объекта, теперь заполняет глобальный скоуп или падает с ошибкой.

Что происходит без new

function Product(name, price) {
  this.name = name;
  this.price = price;
}

// правильно
const shirt = new Product('Футболка', 1500);
console.log(shirt.name);        // => 'Футболка'

// забыли new — в нестрогом режиме
const oops = Product('Штаны', 2500);
console.log(oops);              // => undefined (функция ничего не вернула)
console.log(window.name);       // => 'Штаны' (!)
console.log(window.price);      // => 2500 (!)

Функция без new отработала как простой вызов. this внутри неё — window. this.name = name тихо создало window.name, а сама функция ничего не вернула. В коде «const oops = Product(...)» получаем undefined. Баг сложно найти: логика работает не так, но нет ошибки, чтобы отследить где именно.

В строгом режиме сценарий громче:

'use strict';

function Product(name, price) {
  this.name = name;   // TypeError: Cannot set properties of undefined
  this.price = price;
}

Product('Штаны', 2500);   // TypeError на первой же строке функции

Уже не тихо: ошибка сразу, стек-трейс указывает на строку. Отладить намного проще.

Защита 1: используй class-синтаксис

Классы ES6 (2015 год) добавили в язык защиту от этой ошибки. Вызов класса без new бросает TypeError автоматически, без всякой проверки:

class Product {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }
}

Product('Штаны', 2500);
// TypeError: Class constructor Product cannot be invoked without 'new'

Это самое надёжное решение: движок JavaScript сам следит, чтобы класс всегда вызывался через new. Никакой boilerplate-проверки писать не надо.

Защита 2: new.target

Если по какой-то причине нужно оставить function-style конструктор (не class), внутри можно проверить, был ли вызов через new, через мета-свойство new.target. Оно вернёт саму функцию, если её позвали через new, и undefined в противном случае:

function Product(name, price) {
  if (!new.target) {
    // забыли new — самостоятельно вызовем себя через new
    return new Product(name, price);
  }
  this.name = name;
  this.price = price;
}

// оба вызова работают одинаково
const a = new Product('X', 100);
const b = Product('Y', 200);   // без new, но всё равно новый объект

console.log(a instanceof Product);   // => true
console.log(b instanceof Product);   // => true

Такой паттерн делает функцию «forgiving»: работает и с new, и без него. Использовалось в старых библиотеках вроде jQuery-плагинов, где авторы не хотели заставлять пользователей помнить про new. В современном коде лучше просто использовать class-синтаксис — он даёт более чёткий сигнал об ошибке.

Защита 3: соглашение об именах

Самое базовое — называть конструкторы с заглавной буквы (PascalCase), а обычные функции с маленькой (camelCase):

// конструкторы — PascalCase
class User { /* ... */ }
class Order { /* ... */ }
function Legacy() { /* ... */ }

// обычные функции — camelCase
function computeTotal() { /* ... */ }
function formatDate() { /* ... */ }

Соглашение не гарантия — забыть new всё равно можно — но помогает читать код: если видишь const x = User(...) вместо const x = new User(...), PascalCase-имя должно триггерить внутренний код-ревью. Плюс современные линтеры (ESLint правило new-cap) ловят эту ошибку статически.

Реальность 2026

В современном коде проблема «забыли new» встречается заметно реже, чем в 2010-х. Причины:

  • Class-синтаксис заменил function-style-конструкторы почти везде. Class всегда бросает TypeError без new.
  • Функциональные React-компоненты и hooks убрали большую часть OOP-кода в UI-слое.
  • TypeScript отлавливает вызовы класса без new на этапе компиляции: ошибка Value of type 'typeof MyClass' is not callable.
  • ESLint правило no-new и new-cap в стандартных конфигах.

Но легаси-код с function-конструкторами всё ещё встречается — в jQuery-плагинах, старых Node-модулях, embedded-скриптах. И там знание про «new — обязателен» остаётся ключевым при отладке.