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:
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:
An empty instance object is created.
The new object is implicitly assigned many internal properties common to all objects. See details below.
The new object is passed into constructor
Product
asthis
.The
Product
constructor’sthis
properties and methods are copied to the new object. In our example it’sstatus
andgetSku()
.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.
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