Implementing something is the best way to really understand it. And this post will show how it’s possible to emulate the
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.
__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
constructorwhich 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
prototypeproperty, that is an object or null
- Objects have access to the properties of the
constructorproperty of an object cannot be changed
__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!