Некоторые функции JavaScript создают экземпляры не только при вызове как конструкторы, но также при вызове как функции. Например, RegExp:
const reg1 = new RegExp('\\w+');
const reg2 = RegExp('\\w+');
reg1 instanceof RegExp; // => true
reg2 instanceof RegExp; // => true
reg1.source === reg2.source; // => true
При выполнении new RegExp('\\w+') и RegExp('\\w+') JavaScript создает эквивалентные объекты регулярного выражения.
Использование вызова функции для создания объектов является потенциальной проблемой, потому что некоторые конструкторы могут опускать логику инициализации объекта, когда ключевое слово new отсутствует.
Следующий пример иллюстрирует проблему:
function Animal(type, legs) {
this.type = type;
this.legs = legs;
return this;
}
// Вызов функции
const dog = Animal('Собака', 4);
dog.type; // => 'Собака'
dog.legs; // => 4
dog === window // => true
Animal - это функция, которая устанавливает свойства type и legs для объекта контекста. При выполнении Animal('Собака', 4) возвращается объект dog, который имеет корректные свойства: dog.type равен 'Собака', а dog.legs равен 4.
Можно предположить, что все хорошо работает для создания и инициализации новых объектов.
Однако, this - это window при вызове функции (см. 2.1), поэтому Animal('Собака', 4) устанавливает свойства объекта window. А это ошибка. Новый объект не создается.
Поэтому обязательно используйте оператор new в случаях, когда ожидается вызов конструктора:
function Animal(type, legs) {
if (!(this instanceof Animal)) {
throw Error('Ошибка - неверный вызов');
}
this.type = type;
this.legs = legs;
return this;
}
// Вызов конструктора
const dog = new Animal('Собака', 4);
dog.type; // => 'Собака'
dog.legs; // => 4
dog instanceof Animal // => true
// Вызов функции. Выдаст ошибку.
const spider = Animal('Паук', 12);
new Animal('Собака', 4) работает хорошо: новый объект создается и инициализируется, потому что ключевое слово new присутствует в вызове конструктора.
В функцию-конструктор добавляется проверка: this instanceof Animal, чтобы убедиться, что контекст выполнения является правильным типом объекта; всякий раз, когда Animal('Паук', 12) будет выполняется без new вызова, будет появляться ошибка.