What’s “new” in JavaScript?

Few syntactic features highlight JavaScript’s object oriented nature more than the new operator. Many of us have been using it for years but do we really know what it does? In languages like Java it allocates memory for an instance object, invokes a constructor function, and returns a reference to the object. And the same holds for JavaScript. But with the language’s use of first class functions and prototypal inheritance, there’s much more to new.

In this post I’ll cover the relationship between function instance objects, prototype objects and instance objects, what happens when new invokes functions, and which functions can be used as constructors.


 Function Instances & Prototypes

Before we explore how new works let’s quickly review JavaScript functions. When a function is defined a function instance object is created under the hood, having a prototype property that references it’s prototype object[1]. Here’s an example.

function Product(sku, status) {
    this.getSku = function() { return sku; };
    this.stockStatus = status;
}

In memory, the two objects look like:
funcobjtop.png

Note I defined a method on the prototype object itself by assigning to function’s prototype property,

Product.prototype.getStatus = function() {
     return this.stockStatus;
};

 new Operator

So meet 3 objects.

var product1 = new Product('ABC1234', 'InStock');
var product2 = new Product('DEF5678', 'BackOrder');
var product3 = new Product('GHI9012', 'Discontinued');

For each new Product the following steps occur:

  1. An empty instance object is created.

  2. The new object is implicitly assigned many internal properties common to all objects. See details below.

  3. The new object is passed into constructor Product as this.

  4. The Product constructor’s this properties and methods are copied to the new object. In our example it’s status and getSku().

  5. The new object’s memory address is returned unless a return keyword is supplied. If so, a specified object may be returned instead.

[2]

Taking a closer look at step 2, internal properties on the new object are

Property Value Description
[[Prototype]] The reference to the prototype object.
[[Class]] String indicating kind of object
[[Extensible]] If true, own properties may be added to the object.
[[Put]] Sets a property value
[[Get]] Returns a property value

[3]

I want to focus on the [[Prototype]] property. The Product constructor function’s prototype property which references it’s prototype object is copied to the new object’s [[prototype]] property.

All prototype objects also have a [[prototype]] property. By default it references Object.prototype, inheriting all of Object‘s properties and methods.

We can visualize these objects in memory.

funcobjmid.png

In sum, new invoked Product as a constructor function (Yellow), creating an object (Green), assigning it instance and internal properties including [[prototype]] which point it’s prototype object (Blue), and returning references to product1, product2, and product3. The instance objects now link to the same prototype object thru their internal [[prototype]] property, giving them access to the prototype’s shared methods.


 Using new

The syntax

new ConstructorFunction([args]);

args is an optional comma separated list of values. When there are no arguments, the parenthesis themselves are optional. Constructor functions usually begin with a capital letter.

So why does using new work for some functions but throws TypeError for others?

new Math()        // TypeError on built-in object
new Element()     // TypeError on host object
new HTMLElement() // TypeError on host object

Functions can be invoked with new when they have an internal [[construct]] property. We’ll look thru the lens of object data types to identify which functions have this property. Objects can be grouped into three categories: custom objects are defined by us developers, built-in objects defined by the JavaScript spec, and host objects provided by the host (browser) environment[4].

All custom functions defined by us developers have the [[construct]] property, being able to be invoked with new. Plain and simple.

new Product('QRS1234', 'InStock'); // works

Most built-ins objects can be invoked with new, such as these familiar ones.

new Object() // works
new Array()  // works
new RegExp() // works

But what about Math and JSON?

new Math()   // TypeError
new JSON()   // TypeError

The trick with built-in objects is most have associated constructor functions that can be invoked with new, but those two along with the Global object don’t. There are also built-in functions such as parseInt() and decodeURI() but it’s clear they can’t be invoked with new because they begin with a lowercase letter.

For host objects, only a few have the [[construct]] property.

new XMLHttpRequest() // works
new Screen()         // TypeError
new Node()           // TypeError



In this post we covered it by focusing on function objects, their prototypes and instance objects, the actions taken when a function is invoked using new, and what functions can be used as constructors.

I hope there was something of value for you in this post. I’d greatly enjoy your comments or questions. Please feel free to reach out on LinkedIn


 References

[1] ECMAScript 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-13.2

[2] ECMAScript 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2

[3] ECMAScript 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-8.6.2

[4] ECMAScript 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.7


 
51
Kudos
 
51
Kudos

Now read this

JavaScript’s Map, Reduce, and Filter

As engineers we build and manipulate arrays holding numbers, strings, booleans and objects almost everyday. We use them to crunch numbers, collect objects, split strings, search, sort, and more. So what’s the preferred way to traverse... Continue →