JavaScript's new operator

Replicating the JavaScript new operator

The other day -- in order to understand it a little better -- I wrote a method to replicate the behavior of the new operator, and I thought I'd share.

Let's begin with the specification as that is usually a good starting point:

11.2.2 The new Operator

The production NewExpression : new NewExpression is evaluated as follows:

  1. Let ref be the result of evaluating NewExpression.
  2. Let constructor be GetValue(ref).
  3. If Type(constructor) is not Object, throw a TypeError exception.
  4. If constructor does not implement the [[Construct]] internal method, throw a TypeError exception.
  5. Return the result of calling the [[Construct]] internal method on constructor, providing no arguments (that is, an empty list of arguments).

The production MemberExpression : new MemberExpression Arguments is evaluated as follows:

  1. Let ref be the result of evaluating MemberExpression.
  2. Let constructor be GetValue(ref).
  3. Let argList be the result of evaluating Arguments, producing an internal list of argument values (11.2.4).
  4. If Type(constructor) is not Object, throw a TypeError exception.
  5. If constructor does not implement the [[Construct]] internal method, throw a TypeError exception.
  6. Return the result of calling the [[Construct]] internal method on constructor, providing the list argList as the argument values.

Hmm...not the clear and straightforward explanation we were hoping for. Let's check out what the Mozilla Developer Network has to say:

Summary

The new operator creates an instance of a user-defined object type or of one of the built-in object types that has a constructor function.

Syntax
new constructor[([arguments])]
Parameters

constructor: A function that specifies the type of the object instance.

arguments: A list of values that the constructor will be called with.

MDN goes on to tell us:

When the code new Foo(...) is executed, the following things happen:

  • A new object is created, inheriting from Foo.prototype. The constructor function Foo is called with the specified arguments and this bound to the newly created object. new Foo is equivalent to new Foo(), i.e. if no argument list is specified, Foo is called without arguments.
  • The object returned by the constructor function becomes the result of the whole new expression. If the constructor function doesn't explicitly return an object, the object created in step 1 is used instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.)

Great! Let's look at a quick example

// Always capitalize the names of your constructor functions
function Car( color ) {
  this.color = color;
}

Car.prototype = {
  honk: function() {
    console.log( "beep, beep" );
  }
};

var theRav = new Car( "teal" ); // I used to roll in a teal rav4

// we can set some other properties if we want--it doesn't affect the prototype chain.
theRav.rims = "26 inches"; // I didn't actually have twenty-sixes
theRav.spinners = true; // also fictitious

theRav.color; // "teal"
theRav.hasOwnProperty( 'color' ); // true, because color is defined on theRav

theRav.honk(); // "beep beep"
theRav.hasOwnProperty( 'honk' ); // false, because honk is defined on theRav's constructor function's prototype, not on theRav itself.
// of course the ones we assigned directly are on the object itself

theRav.hasOwnProperty( 'rims' ); // true
theRav.hasOwnProperty( 'spinners' ); // true

So, what's happening? Well, MDN tells us that calling a function with the new operator creates an object. Great, so now we have {}. Well, it also inherits from Foo.prototype. What this does is create a property called proto on our newly created object. This is the prototype property of the constructor function. (You'll notice that the constructor function (Car)'s prototype is actually equal to the proto property:
Car.prototype === theRav.proto // true
So we actually have:

{
  __proto__: {
                honk: function(){ console.log( 'beep, beep' ) },
                __proto__: Object.prototype // this is because Car.prototype inherits from Object.prototype
              }
}

Next, the "ThisBinding" is set to our newly created object and the constructor function is called. So that object just mentioned gets bound to this and Car( 'teal' ) is called. Car( 'teal' ) sets the color property on this equal to 'teal'. Lastly, because Car does not explicitly return an object, the object created above is returned instead. So, from calling new Car( 'teal' ) we get back:

{
  color: "teal",
  __proto__: {
                honk: function(){ console.log( 'beep, beep' ) },
                __proto__: Object.prototype
              }
}

Okay awesome. One last note before the last step, don't go messing around with the proto property and setting it to whatever you feel like. Most people make a point not to alter the proto property because it often leads to trouble down the line.

Alright, let's finish what we set out to do in the first place: replicate the new operator. Obviously this isn't anything we would use in real life (it already exists!), but from the standpoint of understanding, it's a good excercise.

First, assuming no arguments are passed to the constructor function
//  assuming no arguments
function myNew(Constructor) {
    var that = {};
    that.__proto__ = Constructor.prototype;
    Constructor.call(that);
    return that;
}

function FooMaker(tree) {
    this.foo = "bar";
}

var fromNew = new FooMaker;
var fromMine = myNew( FooMaker );

// logging them, we can see they're pretty much identical
console.log( fromNew );
console.log( fromMine );

We instantiate an object, set it's proto property equal to its constructor function's prototype and then call the constructor function, setting the ThisBinding equal to our newly instantiated object. There's only one problem: If the constructor function returns an object, we need to return the object instead of that. And we have to account for the caveat that typeof null === 'object' // true, but the new operator will still return this if the constructor returns null.We update the code:

//  assuming no arguments

function myNew(Constructor) {
    var result;
    var that = {};
    that.__proto__ = Constructor.prototype;
    return ('object' === typeof (result = Constructor.call(that))) && (result !== null) ?
      result : that;
}

function FooMaker(tree) {
    this.foo = "bar";
}

var fromNew = new FooMaker;
var fromMine = myNew( FooMaker );

// logging them, we can see they're pretty much identical
console.log( fromNew );
console.log( fromMine );

Perfect! But what if our constructor function takes arguments? Well, let's just store those in an array and apply our constructor with them. Here's the final code:

// with arguments
function myNew(Constructor) {
    var result;
    var that = {};
    // if only arguments had a shift method..
    var args = [];
    for(var i = 1; i < arguments.length; i++){
        args.push(arguments[i]);
    }
    that.__proto__ = Constructor.prototype;
    return ('object' === typeof (result = Constructor.apply(that, args))) && (result !== null) ?
        result : that;
}

// try returning other things from the constructor if you like
function FooBar(tree) {
    this.foo = "bar";
    this.tree = tree;
}

var fromNew = new FooBar("maple");
var fromMine = myNew(FooBar, "maple");

// logging them, we can see they're pretty much identical
console.log( fromNew );
console.log( fromMine );

Note: some of you might be looking at the return statement like "?!?!". It probably would have been much better and clearer to do the following:

result = Constructor.apply( that, args );
if ( typeof result === 'object' && result !== null ) {
  return result;
}
else{
  return that;
}

Not doing this saved a few lines of code, but probably cost some readability; In general I think it's usually more important to be clear than elegant, but that's a debate for another day.