Not every JavaScript function is constructable
- Published at
- Updated at
- Reading time
- 2min
Šime Vidas recently tweeted about the object method shorthand definition. The tweet described that shorthand method defintions are not constructable and can not be used with the new
keyword.
You can’t invoke an object method as a constructor if you’ve used the shorthand syntax.
I'm not using the new
keyword very often these days but this fact surprised me. So I started digging the EcmaScript spec to figure out what differences arrow functions and the shorthand have in comparison to function property definitions.
There are three ways to define a method in an object and they're not only syntatically different but also behave differently.
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.
There are "essential internal methods" and these range from e.g. [[GetPrototypeOf]]
to [[OwnPropertyKeys]]
.
When we're dealing with functions (and remember these are objects, too) there can be also "additional Essential Internal Methods" which include [[Call]]
and [[Construct]]
. [[Construct]]
is what is used when we use new
or super
to create a new object.
It turns out though that not every function includes [[Construct]]
which means that not every function is a constructor function.
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.
Looking at the definition of the new
operations we'll see that it is supposed to throw a TypeError
whenever isConstructor
is false. isContructor
looks up the [[Construct]]
internal method.
If IsConstructor (constructor) is false, throw a TypeError exception.
So, let's look at the following three lines of code and see what happens when we want to use the functions Fn
, Arrow
and Shorthand
as a constructor:
const example = {
Fn: function() { console.log(this); },
Arrow: () => { console.log(this); },
Shorthand() { console.log(this); }
};
new example.Fn(); // Fn {}
new example.Arrow(); // Uncaught TypeError: example.Arrow is not a constructor
new example.Shorthand(); // Uncaught TypeError: example.Shorthand is not a constructor
That's the surprising part of Šime's tweet.
The definition for every function creation goes down to FunctionCreate
defined in the EcmaScript spec.
The spec for FunctionCreate
is very clear:
FunctionCreate (kind, ParameterList, Body, Scope, Strict, prototype)
[...]
1. If the prototype argument was not passed, then
a. Let prototype be the intrinsic object %FunctionPrototype%.
2. If "kind" is not Normal, let allocKind be "non-constructor".
[...]
So it turns out that only functions of type Normal
will be constructable and will implement [[Construct]]
. Reading the spec further you'll find that arrow functions use kind Arrow
and method shorthand definitions use kind Method
. This results in them being a "non-constructor".
That's it and this is where this behavior come from.
Join 5.5k readers and learn something new every week with Web Weekly.