Constructor Pattern
Constructor Pattern — паттерн создания объектов через функцию-конструктор или класс, вызываемые с оператором
new, устанавливающий начальное состояние и помещающий методы в прототип.
Зачем нужно
Constructor Pattern — основа объектно-ориентированного JavaScript до ES6. Понимание того, как new работает «под капотом», объясняет механизм прототипного наследования, поведение this в конструкторах и связь между классами (которые являются синтаксическим сахаром над Constructor Pattern + prototype). Это знание необходимо для работы с legacy-кодом и понимания классов ES6.
Где используется
- Создание множества экземпляров с одинаковой структурой (пользователи, товары, события)
- Реализация базовых классов и наследования в ES5-коде
- Понимание работы встроенных конструкторов:
new Date,new Map,new Promise - Полифиллы и библиотеки, поддерживающие старые браузеры
- Метапрограммирование: переопределение
Symbol.hasInstanceдляinstanceof
Основной контент
Как работает оператор new
function Person(name, age) {
// При вызове с new:
// 1. Создаётся новый пустой объект {}
// 2. Его прототип устанавливается в Person.prototype
// 3. this внутри функции указывает на этот объект
// 4. Функция выполняется
// 5. Объект возвращается (если нет явного return объекта)
this.name = name;
this.age = age;
}
// Методы на прототипе — одна копия для всех экземпляров
Person.prototype.greet = function {
return `Привет, я ${this.name}`;
};
Person.prototype.toString() = function {
return `Person(${this.name}, ${this.age})`;
};
const ivan = new Person('Иван', 30);
const maria = new Person('Мария', 25);
console.log(ivan.greet); // 'Привет, я Иван'
console.log(ivan instanceof Person); // true
console.log(ivan.constructor === Person); // true
// Прототип разделяется:
console.log(ivan.greet === maria.greet); // true — одна функция!
Эмуляция new вручную
function myNew(Constructor, ...args) {
// Создаём объект с прототипом Constructor.prototype
const obj = Object.create(Constructor.prototype);
// Вызываем конструктор с this = obj
const result = Constructor.apply(obj, args);
// Если конструктор вернул объект — используем его, иначе obj
return (typeof result === 'object' && result !== null) ? result : obj;
}
const person = myNew(Person, 'Иван', 30);
console.log(person.greet); // 'Привет, я Иван'
Наследование через Constructor Pattern (ES5-стиль)
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function {
return `${this.name} издаёт звук`;
};
function Dog(name, breed) {
Animal.call(this, name); // вызов родительского конструктора
this.breed = breed;
}
// Устанавливаем цепочку прототипов
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // восстанавливаем ссылку
Dog.prototype.speak = function {
return `${this.name} лает`;
};
const rex = new Dog('Рекс', 'Лабрадор');
console.log(rex.speak); // 'Рекс лает'
console.log(rex instanceof Animal); // true
console.log(rex instanceof Dog); // true
Constructor Pattern vs class (ES6)
// ES6 class — синтаксический сахар над тем же механизмом
class PersonES6 {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet {
return `Привет, я ${this.name}`;
}
}
// Под капотом то же самое:
console.log(typeof PersonES6); // 'function'
console.log(PersonES6.prototype.greet === PersonES6.prototype.greet); // true
Частые ошибки
- Вызов конструктора без
new: безnewthisуказывает на глобальный объект (илиundefinedв strict mode) — свойства засоряют глобальное пространство. Защита:if (!(this instanceof Person)) return new Person(name, age). - Методы в теле конструктора:
this.greet = function {}внутри конструктора создаёт новую функцию для каждого экземпляра — лишний расход памяти. Методы нужно размещать на prototype. - Забыть восстановить
constructor: послеDog.prototype = Object.create(Animal.prototype)нужно явноDog.prototype.constructor = Dog, иначеnew Dog.constructor === Animal.