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?
A constructor is a special function that creates and initializes an object instance of a class. In JavaScript, a constructor gets called when an object is created using the new keyword. The purpose of a constructor is to create a new object and set values for any existing object properties.
Function objects created with the Function constructor are parsed when the function is created. This is less efficient than creating a function with a function expression or function declaration and calling it within your code, because such functions are parsed with the rest of the code.
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.
A constructor is a special kind of method from where execution starts in side a class. Where as a function is a normal kind of method & used to provide some functionality. A function may or may not return value where as a constructor must not return value. constructor doesn't have a return type where as a function has.
A little bit of background:
ECMAScript 6+ distinguishes between callable (can be called without new
) and constructable (can be called with new
) functions:
class
syntax are not callable.Function
constructor) are callable and constructable.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()');
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With