Implementing something is the best way to really understand it. And this post will show how it’s possible to emulate the new
JavaScript operator. Developing a language functionality using itself is a nice way to see how flexible it is.
Everything is vague to a degree you do not realize till you have tried to make it precise.
-- Bertrand russell
First of all, let’s define a simple constructor function to play with.
Can’t get simpler than that.
Second, let’s define our newNew
interface. It’s not possible to define an operator, so a function that receives a constructor function and returns a new instance of that object will do.
Okay, that’s a start. But the function is simply returning a new object literal, not an instance of the constructor function, Person
. Which leads to the third step. Prototypes.
The __proto__
property exposes the Prototype of an object. The prototype
property is exclusive to functions. It’s important to say that the __proto__
property is deprecated and should not me used in production code, ES6 provides Object.setPrototypeOf
to mutate an object prototype, more information can be found here.
Prototypes are a convenient way to define types of objects. The key points are:
- Objects have an implicit property called
constructor
which is a function, and tells which constructor function the object was created from (like Array, Number, Date, or Person in the example) - Functions have the
prototype
property, that is an object or null - Objects have access to the properties of the
prototype
of itsconstructor
- The
constructor
property of an object cannot be changed - The
__proto__
property exposes the object’s prototype
This is indeed confusing, and further reading about prototypes is advised.
With this in mind, let’s move forward. The new line defines the new object’s type, and the test newNew(Person) instanceof Person
will be now true
. But there’s still one thing missing, what if the constructor function takes arguments for its initialization?
In this case, our newNew
function can’t handle the arguments. Fortunately, that’s an one liner easy fix. And here is the full final code.
The last added line takes care of calling the constructor function with it’s context (the this
value) set to the new object using .apply
. The long .slice(1)
call gets all arguments but the constructor itself and return them as an array.
And finally the newNew
works as expected!