I know there are maybe a million similar questions out there already, e.g.,
- here: what is the new keyword
- here: typeerror: x is not a constructor
- here:
__proto__
vs prototype - here: MDN doc: new operator
but please hear me out.
The code:
let f = function(){console.log(".f.")};
fn = new f();
// Now:
typeof fn === "object" //true
//! fn() //TypeError: fn is not a function
//! new fn() //TypeError: fn is not a constructor
The general question would be: is it possible to create a "newable" object fn
, by manipulating the functionf
.
The question breaks down to the internal of the "new" keywords.
My doubt is, according to the MDN document, when a new
keyword is used, the constructor
of an class
or function
is called. However, even though fn.__proto__.constructor === f
is true like all other javascript functions
, fn
is of type'object'
(can we somehow alter it to 'function'
?), and new fn()
throws TypeError.
We can even add more to the mix by doing:
fn.constructor = f.constructor
fn.__proto__ = f.__proto__
fn.prototype = f.prototype
// f.constructor === Function //true
//! fn.call(this) //fn.call is not a function
still, fn()
won't work, neither does new fn
or new fn()
.
Why?
The new-able objects in JavaScript are:
function
keyword (excluding generator functions)I know this because the only object types the
new
operator works with are "constructors" (specification term). A constructor is an object with a[[Construct]]
internal method, and you can search the ECMAScript specification to find out which kinds of object have a[[Construct]]
internal method.To make the result of a constructor function new-able, therefore, you must return one of the object kinds listed above.
Note that the specification specifically says that all constructors are definitionally functions because they must support the
[[Call]]
internal method (note also the caveat below about host objects).If you want to get very advanced, then you may be interested to learn that host objects do not appear to share the ordinary limitations for constructors (presumably for legacy Web compatibility reasons), but these are exceptional.
Explanation of the
.constructor
propertyWhen a new-able function
f
is declared, two objects are created: the function-objectf
itself, and a default object on the.prototype
own-property off
. The.constructor
property of this default.prototype
object is automatically set by the runtime to bef
. I believe classes work in a very similar fashion. Note that the fact that the name of this property was chosen to be "prototype" makes discussing prototypes quite confusing in JavaScript (as it is distinct from the[[prototype]]
of the function).This
constructor
own-property on the object positioned on the.prototype
property, is never read by any built-in function or operation (that I know of). I view it as vestigial from the earliest days of JavaScript - it's original intent was as a way to maintain a link between the "class" that constructed an object as a developer affordance. Host environments (eg browsers) sometimes use it to infer the "type" of an object for the purposes of communicating with the user (eg. console output), the property is writeable and therefore unreliable.Steps performed by the
new
operatorAt a high level, when
new
is invoked against a constructor, the following steps occur (spec contains full details):o
is created[[Prototype]]
("the prototype") ofo
is set to the value of the.prototype
property of the constructor (note this means the.constructor
property is inherited by the new object)this
) of the constructor body is set too
this
value defined aboveo
is returned by default