Ne každá funkce JavaScriptu je konstruovatelná

Šime Vidas nedávno tweetoval o zkrácené definici objektové metody. Tweet popsal, že zkrácené definice metody nejsou konstruovatelné a nelze je použít s new klíčové slovo.

Nepoužívám new klíčové slovo v dnešní době velmi často, ale tato skutečnost mě překvapila. Začal jsem tedy zkoumat specifikaci EcmaScript, abych zjistil, jaké rozdíly mají funkce šipek a zkratka ve srovnání s definicemi vlastností funkcí.

Existují tři způsoby, jak definovat metodu v objektu, a jsou nejen syntaticky odlišné, ale také se odlišně chovají.

"konstruovatelné rozdíly"

Při čtení specifikace se ukazuje, že objekty JavaScriptu mají interní metody, které definují jejich specifické chování.

Existují "základní interní metody" a ty se pohybují např. [[GetPrototypeOf]][[OwnPropertyKeys]] .

Když se zabýváme funkcemi (a pamatujte si, že se jedná také o objekty), mohou existovat také "další základní interní metody", které zahrnují [[Call]] a [[Construct]] . [[Construct]] je to, co se používá, když používáme new nebo super vytvořit nový objekt.

Ukazuje se však, že ne každá funkce obsahuje [[Construct]] což znamená, že ne každá funkce je funkcí konstruktoru.

Při pohledu na definici new operací uvidíme, že má házet TypeError kdykoli isConstructor je nepravdivé. isContructor vyhledá [[Construct]] interní metoda.

Podívejme se tedy na následující tři řádky kódu a uvidíme, co se stane, když chceme použít funkce Fn , Arrow a Shorthand jako konstruktor:

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

To je ta překvapivá část Šimeho tweetu.

Definice pro každé vytvoření funkce klesá na FunctionCreate definované ve specifikaci EcmaScript.

Specifikace pro FunctionCreate je velmi jasné:

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".
[...]

Ukazuje se tedy, že pouze funkce typu Normal bude konstruovatelný a bude implementovat [[Construct]] . Při dalším čtení specifikace zjistíte, že funkce šipek používají typ Arrow a zkrácené definice metod používají druh Method . To má za následek, že jsou "nekonstruktoři".

To je ono a odtud toto chování pochází.