Проверка типа экземпляра [this] в конструкторе - это путь. Проблема в том, что без лишних слов этот подход подвержен ошибкам. Однако есть решение.
Допустим, мы имеем дело с функцией ClassA (). Примитивный подход:
function ClassA() {
if (this instanceof arguments.callee) {
console.log("called as a constructor");
} else {
console.log("called as a function");
}
}
Существует несколько способов, которые вышеупомянутое решение не будет работать, как ожидалось. Рассмотрим только эти два:
var instance = new ClassA;
instance.classAFunction = ClassA;
instance.classAFunction(); // <-- this will appear as constructor call
ClassA.apply(instance); //<-- this too
Чтобы преодолеть это, некоторые предлагают: а) поместить некоторую информацию в поле экземпляра, например, «ConstructorFinished», и проверить его, или б) отслеживать ваши построенные объекты в списке. Мне неудобно с обоими, так как изменение каждого экземпляра ClassA слишком инвазивно и дорого для работы функции, связанной с типом. Сбор всех объектов в списке может вызвать проблемы с сборкой мусора и ресурсами, если в ClassA будет много экземпляров.
Нужно уметь контролировать выполнение вашей функции ClassA. Простой подход:
function createConstructor(typeFunction) {
return typeFunction.bind({});
}
var ClassA = createConstructor(
function ClassA() {
if (this instanceof arguments.callee) {
console.log("called as a function");
return;
}
console.log("called as a constructor");
});
var instance = new ClassA();
Это эффективно предотвратит все попытки обмануть значение [this]. Связанная функция всегда будет сохранять свой исходный [this] контекст, если вы не вызовете ее с помощью оператора new .
Расширенная версия возвращает возможность применять конструктор к произвольным объектам. В некоторых случаях это может быть использование конструктора в качестве преобразователя типов или предоставление вызываемой цепочки конструкторов базового класса в сценариях наследования.
function createConstructor(typeFunction) {
var result = typeFunction.bind({});
result.apply = function (ths, args) {
try {
typeFunction.inApplyMode = true;
typeFunction.apply(ths, args);
} finally {
delete typeFunction.inApplyMode;
}
};
return result;
}
var ClassA = createConstructor(
function ClassA() {
if (this instanceof arguments.callee && !arguments.callee.inApplyMode) {
console.log("called as a constructor");
} else {
console.log("called as a function");
}
});