72

I noticed not all the Javascript functions are constructors.

var obj = Function.prototype;
console.log(typeof obj === 'function'); //true
obj(); //OK
new obj(); //TypeError: obj is not a constructor

Question 1: How do I check if a function is a constructor so that it can be called with new keyword?

Question 2: When I create a function, is it possible to make it NOT a constructor?

4
  • Interesting are Function and Function.prototype the only function's that aren't constructors? Commented Dec 1, 2016 at 23:58
  • 2
    Function is a construtor. new Function(); works. Commented Dec 2, 2016 at 0:11
  • Just check if the type is a function. Commented Dec 2, 2016 at 0:17
  • Symbol too is not a function constructor. console.log(new Symbol); //TypeError: Symbol is not a constructor Commented Jan 31, 2021 at 9:31

13 Answers 13

66

A little bit of background:

ECMAScript 6+ distinguishes between callable (can be called without new) and constructable (can be called with new) functions:

  • Functions created via the arrow functions syntax or via a method definition in classes or object literals are not constructable.
  • Functions created via the class syntax are not callable.
  • Functions created in any other way (function expression/declaration, Function constructor) are callable and constructable.
  • Built-in functions are not constructrable unless explicitly stated otherwise.

About Function.prototype

Function.prototype is a so called built-in function that is not constructable. From the spec:

Built-in function objects that are not identified as constructors do not implement the [[Construct]] internal method unless otherwise specified in the description of a particular function.

The value of Function.prototype is create at the very beginning of the runtime initialization. It is basically an empty function and it is not explicitly stated that it is constructable.


How do I check if an function is a constructor so that it can be called with a new?

There isn't a built-in way to do that. You can try to call the function with new, and either inspect the error or return true:

function isConstructor(f) {
  try {
    new f();
  } catch (err) {
    // verify err is the expected error and then
    return false;
  }
  return true;
}

However, that approach is not failsafe since functions can have side effects, so after calling f, you don't know which state the environment is in.

Also, this will only tell you whether a function can be called as a constructor, not if it is intended to be called as constructor. For that you have to look at the documentation or the implementation of the function.

Note: There should never be a reason to use a test like this one in a production environment. Whether or not a function is supposed to be called with new should be discernable from its documentation.

When I create a function, how do I make it NOT a constructor?

To create a function is truly not constructable, you can use an arrow function:

var f = () => console.log('no constructable');

Arrow functions are by definition not constructable. Alternatively you could define a function as a method of an object or a class.

Otherwise you could check whether a function is called with new (or something similar) by checking it's this value and throw an error if it is:

function foo() {
  if (this instanceof foo) {
    throw new Error("Don't call 'foo' with new");
  }
}

Of course, since there are other ways to set the value of this, there can be false positives.


Examples

function isConstructor(f) {
  try {
    new f();
  } catch (err) {
    if (err.message.indexOf('is not a constructor') >= 0) {
      return false;
    }
  }
  return true;
}

function test(f, name) {
  console.log(`${name} is constructable: ${isConstructor(f)}`);
}

function foo(){}
test(foo, 'function declaration');
test(function(){}, 'function expression');
test(()=>{}, 'arrow function');

class Foo {}
test(Foo, 'class declaration');
test(class {}, 'class expression');

test({foo(){}}.foo, 'object method');

class Foo2 {
  static bar() {}
  bar() {}
}
test(Foo2.bar, 'static class method');
test(new Foo2().bar, 'class method');

test(new Function(), 'new Function()');

Sign up to request clarification or add additional context in comments.

8 Comments

If constructor has parameters isConstructor returns false.
'Whether or not a function is supposed to be called with new should be discernible from its documentation.'- not at all. JS is a duck-typed language. There are valid scenarios where one needs to do some sort of run-time 'type' checking on parameters and whether a function is constructable is one of those.
What about const isConstructor = fn => typeof fn === 'function' && 'prototype' in fn ? I can see arrow functions don't have a prototype
@Frondor: That would probably be good enough for 99% of all cases. I still have to add that it's of course possible to manually define a prototype property on an arrow function. Also see this comment: stackoverflow.com/questions/40922531/…
By the way. Classes are 'callable'. They implement [[Call]] internal, they just throw.
|
37

You are looking for if a function has a [[Construct]] internal method. The internal method IsConstructor details the steps:

IsConstructor(argument)

ReturnIfAbrupt(argument).  // (Check if an exception has been thrown; Not important.)  
If Type(argument) is not Object, return false.  // argument === Object(argument), or (typeof argument === 'Object' || typeof argument === 'function')  
If argument has a [[Construct]] internal method, return true.  
Return false.

Now we need to find places where IsConstructor is used, but [[Construct]] isn't called (usually by the Construct internal method.)

I found that it is used in the String function's newTarget (new.target in js), which can be used with Reflect.construct:

function is_constructor(f) {
  try {
    Reflect.construct(String, [], f);
  } catch (e) {
    return false;
  }
  return true;
}

(I could have used anything really, like Reflect.construct(Array, [], f);, but String was first)

Which yields the following results:

// true
is_constructor(function(){});
is_constructor(class A {});
is_constructor(Array);
is_constructor(Function);
is_constructor(new Function);

// false
is_constructor();
is_constructor(undefined);
is_constructor(null);
is_constructor(1);
is_constructor(new Number(1));
is_constructor(Array.prototype);
is_constructor(Function.prototype);
is_constructor(() => {})
is_constructor({method() {}}.method)

<note>

The only value that I found it didn't work for is Symbol, which, although new Symbol throws a TypeError: Symbol is not a constructor in Firefox, is_constructor(Symbol) === true. This is technically the correct answer, as Symbol does have a [[Construct]] internal method (Which means it can also be subclassed), but using new or super is special cased for Symbol to throw an error (So, Symbol is a constructor, the error message is wrong, it just can't be used as one.) You can just add if (f === Symbol) return false; to the top though.

The same for something like this:

function not_a_constructor() {
  if (new.target) throw new TypeError('not_a_constructor is not a constructor.');
  return stuff(arguments);
}

is_constructor(not_a_constructor);  // true
new not_a_constructor;  // TypeError: not_a_constructor is not a constructor.

So the intentions of the function of being a constructor can't be gotton like this (Until somthing like Symbol.is_constructor or some other flag is added).

</note>

4 Comments

This is a clever solution, but aside from Function.prototype, you would get the same results by checking whether the typeof f === "function".
@JeffRose Sorry, I didn't seem to add enough negative examples. Specifically the arrow function. is_constructor(() => {}) is false.
This only works for constructors without params I believe. is_constructor(URL); will fail for instance
@LodeMichels This does work with constructors with parameters. Not sure why it wouldn't. is_constructor(URL) returns true.
25

With ES6+ Proxies, one can test for [[Construct]] without actually invoking the constructor. Here's a snippet:

const handler={construct(){return handler}} //Must return ANY object, so reuse one
const isConstructor=x=>{
    try{
        return !!(new (new Proxy(x,handler))())
    }catch(e){
        return false
    }
}

If the passed item isn't an object, the Proxy constructor throws an error. If it's not a constructable object, then new throws an error. But if it's a constructable object, then it returns the handler object without invoking its constructor, which is then not-notted into true.

As you might expect, Symbol is still considered a constructor. That's because it is, and the implementation merely throws an error when [[Construct]] is invoked. This could be the case on ANY user-defined function that throws an error when new.target exists, so it doesn't seem right to specifically weed it out as an additional check, but feel free to do so if you find that to be helpful.

1 Comment

Thank you for this excellent answer!
18

There is a quick and easy way of determining if function can be instantiated, without having to resort to try-catch statements (which can not be optimized by v8)

function isConstructor(obj) {
  return !!obj.prototype && !!obj.prototype.constructor.name;
}
  1. First we check if object is part of a prototype chain.
  2. Then we exclude anonymous functions

There is a caveat, which is: functions named inside a definition will still incur a name property and thus pass this check, so caution is required when relying on tests for function constructors.

In the following example, the function is not anonymous but in fact is called 'myFunc'. It's prototype can be extended as any JS class.

let myFunc = function () {};

7 Comments

Why would you want to exclude anonymous functions ? They are constructables.
I would prefer Object.hasOwnProperty("prototype") for this function. BTW, while most constructables do have .prototype's, some like bound functions don't, but they are still constructables. This is because constructability does not have a direct relationship with .prototype, it all has to do with the internal [[construct]] method. So, while this is a decent solution for many cases, it is not entirely bulletproof (as you have noted).
isConstructor(class {}) returns false
@doubleOrt In addition to that, having a prototype property does not mean that function is a constructor. For example, built in Symbol has a prototype property. However, new Symbol() throws not a constructor TypeError.
Isn't it kind of silly for the ECMAScript specification to specify rules and implementation steps (like testing for [[construct]] that don't have a clear implementation in regular ECMAScript? Somehow that seems a bit broken. The best we can apparently do is to approximate it for most scenarios. I'm trying to implement a polyfill for a proposed new method and trying to follow the proposed specification, but that apparently isn't' entirely possible because there's no way to test if something truly is a constructor.
|
6

If the function is a constructor then it will have a "prototype" member which in turn has a "constructor" member that is equal to the function itself.

function isConstructor(func) {
    return typeof func === 'function' && !!func.prototype && func.prototype.constructor === func;
}

4 Comments

Please fix the parenthesis. Try isConstructor(false). SCNR :-)
And to quote @doubleOrt: "while most constructables do have .prototype's, some like bound functions don't, but they are still constructables. This is because constructability does not have a direct relationship with .prototype"
Above is named isConstructor not is isConstructable, I mean, it's okay that this will return false for Bound-constructors, because "bound" means to redirect to something, instead of being a real constructor directly.
Just an observation but a function's prototype property can be a prototype other than its own prototype if its reassigned which means that it can have a constructor property that is not equal to the original function. So please help me understand why you have that last constructor check. I see no point since all you really need to check is whether the prototype property exists and is an object.
3

There is a quick and easy way of determining if function can be instantiated, without having to resort to try-catch statements, which can not be optimized by v8 (until 2017), and which requires the constructor to support said try-catch.

function isConstructor(value) {
    return !!value && !!value.prototype && !!value.prototype.constructor;
}
  1. First, we check value is truthy.
  2. Then checks if value is part of a prototype chain.
  3. At last, simply checks if constructor is set ;-)

Note that above is named isConstructor not is isConstructable, I mean, this will return false for Bound-constructors as said in comments, because "bound" means to redirect to something, instead of being a real constructor directly.

So this answers title's "check ... is constructor" question, but not later "check ... can be called with new" question.

Example:

const myClazz = {method() {}};
myClazz.method = myClazz.method.bind(myClazz);

// We can call above with new keyword.
new (myClazz.method);

// But it's just a callback.
if (isConstructor(myClass))
   throw new Error('expected to return false for arrow-functions and similar.');

Unit-testing

Below is based on Jasmine.

// Change import to wherever your common functions are 
// (for me they're in src directory, outside of tests directory).
import * as common from '../common-tools';

let isMyClassCalled = false;
class MyClass {
  constructor() {
      isMyClassCalled = true;
  }
}

describe('App isConstructor tool', () => {
    it('should detect constructor', function () {
        detect(class A {});
        detect(Array);
        detect(Function);
        detect(new Function);
        detect({method() {}}.method);
    });

    it('should NOT detect as constructor', function () {
        noDetect();
        noDetect(undefined);
        noDetect(null);
        noDetect(1);
        noDetect(new Number(1));
        noDetect(new (function(){}));
        noDetect(Array.prototype);
        noDetect(Function.prototype);
        // Commented because optimizations convert below into function.
        //noDetect((() => {}));
    });

    it('should NOT detect bound constructors', function () {
        const clazz = {method() {}};
        clazz.method = clazz.method.bind(clazz);
        noDetect(clazz.method);
    });

    it('should never call constructor', function () {
        common.isConstructor(MyClass);
        expect(isMyClassCalled).toBe(false);
    });

    function detect(value, expecting = true) {
        expect(common.isConstructor(value))
            .withContext('For "' + value + '" value')
            .toBe(expecting);
    }

    function noDetect(value) {
        detect(value, false);
    }
});

Alternative

All above tests pass with below as well.

function isConstructor(value) {
    return typeof value === 'function' && !!value.prototype && value.prototype.constructor === value;
}

4 Comments

I believe detect((() => {})) should be in the other test case and be noDetect((() => {})). After all, arrow functions are not constructors. FWIW, stackoverflow.com/a/57862312/218196 and stackoverflow.com/a/53112237/218196 already mentioned this approach.
@FelixKling I just checked and my WebPack was optimizing that into "detect(function () {});", now this approves my answer even more (after I fix the tests), because arrow-functions are auto "bound" ;-)
what about Boolean(value?.prototype?.constructor);?
@joshua.thomas.bird The OP is about JavaScript, but as long as that compiles to my JS code, the results should be same.
1

If speed is what you're after and you're not worried about edge cases:

typeof value=='function' && !!value.prototype

But if you're looking for some level of fool-proofness without using try/catch, this uses a regular expression on the string representation of functions to green light uppercase functions and all user-defined functions which are always constructable, while always ruling out arrow functions. Try/catch is only used in the edge case of bound and some native functions. Works in old browsers.

function isConstructable(value) {
    if (typeof value=='function') {

        // Test if Function.prototype.toString works as expected
        // due to many browsers having unexpected implementations.
        // This test can be moved into global scope.
        var FUNC2STRING = /^\s*function\s*Date\s*\(\)\s*\{\s*\[native code\b/.test(Date) && Date.toString
        
        if (FUNC2STRING) {
            // test for arrow function, else extract name from native function
            var matches = /^[^{*]*(?:\/\*[\s\S]*?\*\/[^{*]*)*=>|^\s*function\s*([\w\$]*)\s*\(\)\s*\{\s*\[native code\b/.exec(FUNC2STRING.call(value));

            if (!matches || /^[A-Z]/.test(matches[1])) {
                // non-native and uppercase functions are always constructable.
                return true;
            }
        }

        // Other native functions must be tested more rigorously
        // since they might be bound functions or otherwise constructable
        if (!matches || matches[1]!=null) {
            // necessary to test bound functions constructability
            try {
                // Throws if target function is not constructable
                typeof Proxy=='function'?
                    new new Proxy(value, {construct:Object})// ES6 needed
                :
                    {} instanceof value;
                return true;
            }
            catch(e) {
            }
        }
        
    }
    return false;
}

If you want the best of both worlds, this prioritizes speed over edge cases while still supporting bound functions. Works in ES6+:

function isConstructableFast(value) {
    if (typeof value=='function') {
        if (value.prototype) {
            var retval = true;
        }
        else if (value.name.slice(0, 6)=='bound ') try {
            retval = new new Proxy(value, {construct:Object});
        }
        catch(e) {
        }
    }
    return !!retval;
}

4 Comments

"Try/catch is slow" - How are you sure your regex solution is faster then?
@DarkBee By far the fastest solution is typeof value=='function' && typeof value.prototype=='object' (editing now to include this). But if my method is more fool proof. Anyway I suggest you choose a different answer as the accepted answer. See my comment about how it won't work with constructors that throw.
I'm not OP - Just pointing out you are making statements without backing them up
try/catch is slower than regex hence the exercise to avoid try/catch. But point taken. See my updated answer for the fastest solution.
0

I tried many workarounds but it didn't satisfy my needs, So i made my own workaround by using reflection metadata.

GOAL: Check if the current Function has it's own __class__ metadata which represent if this function is a constructor or not.

  • npm install reflect-metadata
  • Create class decorator factory @Class()
  • Create a helper function to check __class__ metadata attached to the current function or not.

NOTE: The only way in this workaround to distinguish between Constructor Function and Normal Function or Class is by using Class Decorator @Class()

import 'reflect-metadata';

type Constructor<T = any> = new (...args: any[]) => T;

function Class() {
    return function (target: Constructor) {
        if (!!Reflect.getOwnMetadata('__class__', target)) {
            throw new Error(`Cannot apply @Class decorator on ${target.name} multiple times.`);
        }
        Reflect.defineMetadata('__class__', target, target);
    };
}

function isConstructor<T>(type: Constructor<T>): boolean {
    if (typeof type !== 'function') return false;
    return !!Reflect.getOwnMetadata('__class__', type);
}

/*
 * ------------------
 * Example
 * ------------------
 */

@Class()
class ServiceClass1 {}

class ServiceClass2 {}

function Test() {}

console.log(isConstructor(ServiceClass1)) // true
console.log(isConstructor(ServiceClass2)) // false
console.log(isConstructor(Test)) // false

Comments

0

The simple way in ECMAScript 2015 and later (without calling the function) is to try to make a subclass:

function isConstructor(f)
{
    try
    {
        class test extends f {}
        return true
    }
    catch(e)
    {
        return false
    }
}

function test(f, name) {
    console.log(`${name} is constructable: ${isConstructor(f)}`);
}

test(class foo {}, 'class expression')
test(function (){console.log('fn called')}, 'function expression') // doesn't print
test(x=>0, 'arrow function')  
test({x(){}}.x, 'shorthand method (no function keyword)')

Comments

-1

For question 1, what about this helper?

Function.isConstructor = ({ prototype }) => Boolean(prototype) && Boolean(prototype.constructor)

Function.isConstructor(class {}); // true
Function.isConstructor(function() {}); // true
Function.isConstructor(() => {}); // false
Function.isConstructor("a string"); // false

For question 2, the arrow function is the solution. It cannot be used as a constructor since it does not rely on the same scope as a regular function and does not have a prototype (definition of instances, similar to class definition for real OOP)

const constructable = function() { console.log(this); };
const callable = () => { console.log(this); };

constructable(); // Window {}
callable(); // Window {}
new constructable(); // aConstructableFunction {}
new callable(); // Uncaught TypeError: callable is not a constructor

Comments

-1

As an addition to Felix Kling's answer, even if a function is not constructable, we can still use it like a constructor if it has a prototype property. We can do this with the help of Object.create(). Example:

// The built-in object Symbol is not constructable, even though it has a "prototype" property:
new Symbol
// TypeError: Symbol is not a constructor.
Object.create(Symbol.prototype);
// Symbol {}
//   description: (...)
//   __proto__: Symbol

1 Comment

This doesn't construct a new Symbol object instance. It constructs a plain object with Symbol.prototype as its prototype.
-1

If you need a solution that works in old browsers (ES3+), you can use instanceof because it will throw if the right-side operator is not constructor. More exactly, it will throw if the right-side operator is not a function or has no prototype object which in the most common use case is not a constructor. It also works with constructables created with Function.prototype.bind() because instanceof checks the prototype property of the bound target function.

The Proxy solution provided by Ryan Hanekamp is also incorporated into my answer below because it takes care of edge cases that instanceof does not.

function isConstructable(value) {
    try {
        typeof Proxy=='function'?
            // Throws if value is not constructable
            new new Proxy(value, {construct:Object})// ES6 needed
        :
            // Throws if not function or target function has no prototype object
            {} instanceof value;
        var isConstructable = true;
    }
    catch(e) {
    }
    return !!isConstructable;
}

Comments

-2

here is my solution:

const isFunction = (val) => {
  return typeof val === 'function' || Object.prototype.toString.apply(val) === '[object Function]'
}
/**
 * 检测测试数据是否为 JavaScript 内置函数
 * ========================================================================
 * @method isNativeFunction
 * @param {Function|Object} fn - 要测试的函数
 * @returns {Boolean} - fn 是内置函数,返回 true,否则返回 false;
 */
const isNativeFunction = (fn) => {
  return isFunction(fn) && /\{\s*\[native code\]\s*\}/.test('' + fn)
}
import isFunction from './isFunction'
import isNativeFunction from './isNativeFunction'

/**
 * 检测测试函数是否为构造函数
 * ========================================================================
 * @method isConstructor
 * @category Lang
 * @param {Function|Object} fn - 要测试的(构造)函数
 * @returns {Boolean} - fn 是构造函数,返回 true,否则返回 false;
 */
const isConstructor = (fn) => {
  const proto = fn.prototype
  const constructor = fn.constructor
  let instance

  if (!isFunction(fn) || !proto) {
    return false
  }

  if (
    isNativeFunction(fn) &&
    (constructor === Function || constructor === fn)
  ) {
    return true
  }

  // 判断 fn 是否为 Promise 构造函数
  instance = new fn()

  // 判断 constructor
  return (
    (instance.constructor === fn && instance instanceof fn) ||
    (instance.constructor === Object && instance instanceof Object)
  )
}

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.