Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect if a function is called as constructor?

Given a function:

function x(arg) { return 30; } 

You can call it two ways:

result = x(4); result = new x(4); 

The first returns 30, the second returns an object.

How can you detect which way the function was called inside the function itself?

Whatever your solution is, it must work with the following invocation as well:

var Z = new x();  Z.lolol = x;  Z.lolol(); 

All the solutions currently think the Z.lolol() is calling it as a constructor.

like image 354
Claudiu Avatar asked Dec 15 '08 08:12

Claudiu


People also ask

Is a function a constructor?

In JavaScript, functions can be used as templates for creating other objects. These functions are called constructor functions and have special properties that are different from regular functions.

How do you call a constructor function?

Constructor functions or, briefly, constructors, are regular functions, but there's a common agreement to name them with capital letter first. Constructor functions should only be called using new . Such a call implies a creation of empty this at the start and returning the populated one at the end.

Do functions have constructors?

Function() constructorThe Function constructor creates a new Function object.

What is constructible in JavaScript?

The "constructable differences" Reading the specification it turns out that JavaScript objects have internal methods that define their specific behavior. Each object in an ECMAScript engine is associated with a set of internal methods that defines its runtime behaviour.


2 Answers

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 as this
  • 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"

like image 182
Tim Down Avatar answered Sep 28 '22 08:09

Tim Down


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" 
like image 34
Daniel Weiner Avatar answered Sep 28 '22 07:09

Daniel Weiner