user2527816
user2527816

Reputation: 67

Finding a constructor name in JavaScript

I need to implement two functions in JavaScript: isConstructor(x) and constructorName(x)

isConstructor(x) should return true in case the argument x is a constructor function, and false, otherwise. For example,

isConstructor(Date)      ===  true
isConstructor(Math.cos)  ===  false

constructorName(x) should return the constructor name for each proper constructor function x. So e.g.

constructorName(Date)  ===  'Date'

I can only think of an ugly implementation for both functions, where I create a command string "var y = new x()" which is then called with an eval(command) in a try/catch statement. If that eval call succeeds at all, I call x a constructor. And I retrieve the name of x indirectly by asking for the class name of the prototype of y, something like

var constrName = Object.prototype.toString.call(y).slice(8,-1); // extracts the `ConstrName` from `[object ConstrName]`
return constrName;

But all this is very ugly, of course. How can I do this properly?

Upvotes: 4

Views: 440

Answers (5)

user2527816
user2527816

Reputation: 67

Thank you all for your very clear and helpful answers! I think I do understand the facts now, but I also got some helpful hints that pointed me to ECMAScript details I didn't know, yet. I need to dive into the literature a little more. Thank you, again! Tom

Upvotes: 0

Tsanyo Tsanev
Tsanyo Tsanev

Reputation: 1289

Like many said in JavaScript almost any function object is a constructor (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function). However if you want to distinguish between the native functions that can't be constructors you can try:

function isConstructor(x) {
    return typeof x === 'function' && typeof x.prototype !== 'undefined';
}

Getting the name is definitely trickier, you can use the Function.prototype.toString.apply(x) and match the name, but for example with the jQuery object that would be an empty string, i.e. if it's anonymous function.

Upvotes: 2

RobG
RobG

Reputation: 147363

The definition of a constructor is an Object that implements an internal [[Construct]] method. The only way to test for that is to call it as a constructor and see what happens. You can't test for internal properties other than the [[Class]] property using Object.prototype.toString.

Any user defined ECMAScript function is a constructor by default, e.g.

function foo(){}

can be called as a constructor. A function is an object that implements an internal [[Call]] method and therefore typeof will return "function". But not all functions are constructors, e.g.

typeof document.getElementById  // function

but

new document.getElementById()

will throw a type error because it's not a constructor. There are also constructors that aren't functions:

typeof XMLHttpRequest  // object

XMLHttpRequest is a constructor, but it doesn't have [[Call]] so can't be called as a function:

var x = XMLHttpRequest() // TypeError: XMLHttpRequest isn't a function

Lastly, you could try testing for a prototype property that is an object or function, but that also isn't reliable since a prototype property could be added to pretty much any object, host or native.

So the bottom line is that try..catch is probably the only way.

Upvotes: 1

Ja͢ck
Ja͢ck

Reputation: 173542

There's only an approximation to this supposed problem; whether a function can be invoked using new can't be reliably done without actually trying to perform such a call and check for a TypeError:

function isConstructor(fn)
{
    try {
        var r = new fn(); // attempt instantiation
    } catch (e) {
        if (e instanceof TypeError) {
            return false; // probably shouldn't be called as constructor
        }
        throw e; // something else went wrong
    }
    return true; // some constructors may not return an instance of itself
}

The problems with this:

  1. Any function may choose to throw a TypeError for reasons other than the fact that it shouldn't be called as a constructor;
  2. The constructor may require certain arguments to be passed;
  3. The return value of a constructor doesn't have to meet the requirement that instanceof obj === func;
  4. The call itself causes a side effect.

Upvotes: 1

basilikum
basilikum

Reputation: 10528

If you don't mind creating a test instance of the function you could do the following:

function isConstructor(func) {
    var t = new func();
    return (t instanceof func);
}

BUT: this only workes if the constructor accepts no arguments. It might as well throw an exception. If it does you could try other (not so reliable) things, like checking if the name of the function starts with an uppercase Letter.

Upvotes: 0

Related Questions