How to detect if a function is called as constructor?
NOTE: This is now possible in ES2015 and later. See Daniel Weiner's answer.
I don't think what you want is possible [prior to ES2015]. There simply isn't enough information available within the function to make a reliable inference.
Looking at the ECMAScript 3rd edition spec, the steps taken when new x()
is called are essentially:
- Create a new object
- Assign its internal [[Prototype]] property to the prototype property of
x
- Call
x
as normal, passing it the new object asthis
- If the call to
x
returned an object, return it, otherwise return the new object
Nothing useful about how the function was called is made available to the executing code, so the only thing it's possible to test inside x
is the this
value, which is what all the answers here are doing. As you've observed, a new instance of* x
when calling x
as a constructor is indistinguishable from a pre-existing instance of x
passed as this
when calling x
as a function, unless you assign a property to every new object created by x
as it is constructed:
function x(y) {
var isConstructor = false;
if (this instanceof x // <- You could use arguments.callee instead of x here,
// except in in EcmaScript 5 strict mode.
&& !this.__previouslyConstructedByX) {
isConstructor = true;
this.__previouslyConstructedByX = true;
}
alert(isConstructor);
}
Obviously this is not ideal, since you now have an extra useless property on every object constructed by x
that could be overwritten, but I think it's the best you can do.
(*) "instance of" is an inaccurate term but is close enough, and more concise than "object that has been created by calling x
as a constructor"
As of ECMAScript 6, this is possible with new.target
. new.target
will be set if the function is called with new
(or with Reflect.construct
, which acts like new
), otherwise it's undefined
.
function Foo() {
if (new.target) {
console.log('called with new');
} else {
console.log('not called with new');
}
}
new Foo(); // "called with new"
Foo(); // "not called with new"
Foo.call({}); // "not called with new"
1) You can check this.constructor
:
function x(y)
{
if (this.constructor == x)
alert('called with new');
else
alert('called as function');
}
2) Yes, the return value is just discarded when used in the new
context
NOTE: This answer was written in 2008, when javascript was still in ES3 from 1999. A lot of new functionality has been added since then, so now better solutions exists. This answer is kept for historical reasons.
The benefit of the code below is that you don't need to specify the name of the function twice and it works for anonymous functions too.
function x() {
if ( (this instanceof arguments.callee) ) {
alert("called as constructor");
} else {
alert("called as function");
}
}
Update As claudiu have pointed out in a comment below, the above code doesn't work if you assign the constructor to the same object it has created. I have never written code that does that and have newer seen anyone else do that eighter.
Claudius example:
var Z = new x();
Z.lolol = x;
Z.lolol();
By adding a property to the object, it's possible to detect if the object has been initialized.
function x() {
if ( (this instanceof arguments.callee && !this.hasOwnProperty("__ClaudiusCornerCase")) ) {
this.__ClaudiusCornerCase=1;
alert("called as constructor");
} else {
alert("called as function");
}
}
Even the code above will break if you delete the added property. You can however overwrite it with any value you like, including undefined
, and it still works. But if you delete it, it will break.
There is at this time no native support in ecmascript for detecting if a function was called as a constructor. This is the closest thing I have come up with so far, and it should work unless you delete the property.
Two ways, essentially the same under the hood. You can test what the scope of this
is or you can test what this.constructor
is.
If you called a method as a constructor this
will be a new instance of the class, if you call the method as a method this
will be the methods' context object. Similarly the constructor of an object will be the method itself if called as new, and the system Object constructor otherwise. That's clear as mud, but this should help:
var a = {};
a.foo = function ()
{
if(this==a) //'a' because the context of foo is the parent 'a'
{
//method call
}
else
{
//constructor call
}
}
var bar = function ()
{
if(this==window) //and 'window' is the default context here
{
//method call
}
else
{
//constructor call
}
}
a.baz = function ()
{
if(this.constructor==a.baz); //or whatever chain you need to reference this method
{
//constructor call
}
else
{
//method call
}
}