Конструкторы
Функция-конструктор — обычная функция, вызываемая с оператором
newдля создания нового объекта. Операторnewсоздаёт пустой объект, привязывает к немуthisи возвращает его.
Зачем нужно
Конструкторы — базовый механизм создания объектов с общей структурой и поведением. До ES6-классов это был единственный способ. Понимание конструкторов объясняет, как работают классы «под капотом».
Где используется
Создание экземпляров, паттерн Factory, встроенные конструкторы (Date, RegExp, Error), legacy-код, понимание классов.
Предпосылки
Как работает new
function User(name, age) {
// 1. Создаётся пустой объект: this = {}
// 2. Устанавливается прототип: this.__proto__ = User.prototype
this.name = name; // 3. Добавляются свойства
this.age = age;
// 4. Возвращается this (неявно)
}
const ivan = new User('Иван', 25);
console.log(ivan); // User { name: 'Иван', age: 25 }
console.log(ivan instanceof User); // true
Пошагово
// new User('Иван', 25) эквивалентно:
function simulateNew(Constructor, ...args) {
const obj = Object.create(Constructor.prototype); // шаг 1-2
const result = Constructor.apply(obj, args); // шаг 3
return result instanceof Object ? result : obj; // шаг 4
}
const user = simulateNew(User, 'Иван', 25);
Конструктор с prototype
function Animal(name, sound) {
this.name = name;
this.sound = sound;
}
// Методы в prototype — общие для всех экземпляров
Animal.prototype.speak = function {
return `${this.name} говорит: ${this.sound}`;
};
Animal.prototype.toString() = function {
return `[Animal: ${this.name}]`;
};
const cat = new Animal('Мурка', 'Мяу');
const dog = new Animal('Бобик', 'Гав');
console.log(cat.speak); // "Мурка говорит: Мяу"
console.log(dog.speak); // "Бобик говорит: Гав"
// Один метод на все экземпляры
console.log(cat.speak === dog.speak); // true
Return из конструктора
// Возврат объекта — заменяет this
function Strange() {
this.a = 1;
return { b: 2 }; // this отбрасывается!
}
console.log(new Strange); // { b: 2 }
// Возврат примитива — игнорируется
function Normal() {
this.a = 1;
return 42; // игнорируется
}
console.log(new Normal); // Normal { a: 1 }
// return без значения — возвращается this
function AlsoNormal() {
this.a = 1;
return;
}
console.log(new AlsoNormal); // AlsoNormal { a: 1 }
instanceof
function Car(brand) {
this.brand = brand;
}
const tesla = new Car('Tesla');
console.log(tesla instanceof Car); // true
console.log(tesla instanceof Object); // true (цепочка прототипов)
// instanceof проверяет Car.prototype в цепочке tesla
console.log(Object.getPrototypeOf(tesla) === Car.prototype); // true
Конструктор vs Фабричная функция
Конструктор
function UserConstructor(name) {
this.name = name;
}
UserConstructor.prototype.greet = function {
return `Привет, ${this.name}`;
};
const u1 = new UserConstructor('Иван');
console.log(u1 instanceof UserConstructor); // true
Фабрика
function createUser(name) {
return {
name,
greet {
return `Привет, ${name}`; // замыкание, не this
}
};
}
const u2 = createUser('Мария');
console.log(u2 instanceof createUser); // false — нет прототипной связи
Сравнение
| Аспект | Конструктор | Фабрика |
|---|---|---|
| Вызов | new Fn |
fn |
this |
Автоматический | Не используется |
instanceof |
Работает | Не работает |
| Прототип | Общие методы | Копия методов |
| Приватность | Нужен # или _ | Замыкание |
| Забыл new | Баг (без class) | Нет проблемы |
| Память | Эффективнее (prototype) | Больше (копии методов) |
Защита от вызова без new
function SafeUser(name) {
// Проверка: вызвана ли с new
if (!(this instanceof SafeUser)) {
return new SafeUser(name); // автоматический new
}
this.name = name;
}
const a = new SafeUser('Иван');
const b = SafeUser('Мария'); // тоже работает
console.log(a.name, b.name); // "Иван" "Мария"
// new.target (ES6) — более надёжный способ
function ModernUser(name) {
if (!new.target) {
throw new Error('Используйте new ModernUser');
}
this.name = name;
}
Встроенные конструкторы
// Не рекомендуется для примитивов — используйте литералы
const str1 = new String('hello'); // объект-обёртка
const str2 = 'hello'; // примитив
console.log(typeof str1); // "object"
console.log(typeof str2); // "string"
console.log(str1 == str2); // true (приведение)
console.log(str1 === str2); // false!
// Полезные встроенные конструкторы
const date = new Date();
const regex = new RegExp('\\d+', 'g');
const error = new Error('Что-то пошло не так');
const map = new Map();
const set = new Set();
Конструктор и класс — одно и то же
class MyClass {
constructor(x) { this.x = x; }
method { return this.x; }
}
// Эквивалент:
function MyFunc(x) { this.x = x; }
MyFunc.prototype.method = function { return this.x; };
// Проверка
console.log(typeof MyClass); // "function"
console.log(MyClass.prototype.constructor === MyClass); // true
Частые ошибки
1. Забытый new
function User(name) {
this.name = name; // this === window без new!
}
// const u = User('Иван'); // window.name = 'Иван' (загрязнение глобала)
// console.log(u); // undefined
// Класс защищает от этого:
// class User { constructor(name) { this.name = name; } }
// User('Иван'); // TypeError: cannot be invoked without 'new'
2. Методы в конструкторе вместо prototype
// Плохо — новая функция для каждого экземпляра
function Bad(name) {
this.name = name;
this.greet = function { return this.name; }; // копия!
}
const a = new Bad('A');
const b = new Bad('B');
console.log(a.greet === b.greet); // false — расход памяти
// Хорошо — общий метод в prototype
function Good(name) {
this.name = name;
}
Good.prototype.greet = function { return this.name; };
const c = new Good('C');
const d = new Good('D');
console.log(c.greet === d.greet); // true
3. Перезапись prototype после создания экземпляров
function Foo() {}
const old = new Foo();
Foo.prototype = { newMethod {} };
const newer = new Foo();
console.log(old instanceof Foo); // false! — старый прототип
console.log(newer instanceof Foo); // true
Практика
- Создай конструктор
Car(brand, model, year)с методами в prototype - Напиши свою реализацию
new(функцияmyNew(Constructor, ...args)) - Сравни конструктор и фабричную функцию по памяти и возможностям
- Добавь проверку
new.targetв конструктор - Создай иерархию:
Vehicle→Car→ElectricCarчерез конструкторы и prototype