Checking for the instance type of the [this] within the constructor is the way to go. The problem is that without any further ado this approach is error prone. There is a solution however.
Lets say that we are dealing with function ClassA(). The rudimentary approach is:
function ClassA() {
if (this instanceof arguments.callee) {
console.log("called as a constructor");
} else {
console.log("called as a function");
}
}
There are several means that the above mentioned solution will not work as expected. Consider just these two:
var instance = new ClassA;
instance.classAFunction = ClassA;
instance.classAFunction(); // <-- this will appear as constructor call
ClassA.apply(instance); //<-- this too
To overcome these, some suggest that either a) place some information in a field on the instance, like "ConstructorFinished" and check back on it or b) keep a track of your constructed objects in a list. I am uncomfortable with both, as altering every instance of ClassA is way too invasive and expensive for a type related feature to work. Collecting all objects in a list could provide garbage collection and resource issues if ClassA will have many instances.
The way to go is to be able to control the execution of your ClassA function. The simple approach is:
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 will effectively prevent all attempts to trick with the [this] value. A bound function will always keep its original [this] context unless you call it with the new operator.
The advanced version gives back the ability to apply the constructor on arbitrary objects. Some uses could be using the constructor as a typeconverter or providing an callable chain of base class constructors in inheritance scenarios.
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");
}
});