JavaScript: Objects — Constructors

JavaScript Objects - Constructors

Learning outcomes:

  • What are constructors
  • Creating and working with constructors
  • Internals of a constructor — the [[Construct]] method
  • Constructors with parameters
  • Adding methods to the instance
  • Problem with adding methods inside constructors
  • The instanceof operator
  • The constructor property
  • Calling the constructor of the parent class
  • Static methods
  • Calling constructors as functions

Introduction

JavaScript from day one was an OOP language. What exactly is meant by the term 'OOP language' would be covered step-by-step as we progress through this unit, but at the core, it refers to a language that could model a programming problem in terms of objects.

As we have already been able to appreciate in the prior segment of this course, everything in JavaScript is an object, obviously excluding the primitives. This is evident of the fact that JavaScript revolves around the notion of objects — it's based on an object-oriented model.

Amongst some defining characteristics of OOP languages is that they have things called constructors that initialize instances when created.

JavaScript also provides constructors but they are quite different from the constructors we'll encounter in classical OOP languages such as Java, C++, Python, PHP etc.

People coming to JavaScript from these languages would find the notion of constructors in JavaScript quite nebulous. We'll see all these distinctions in detail in this chapter.

This chapter aims to introduce us to the idea of constructors in JavaScript; how to create our very own constructors and thus emulate classes in classical OOP languages; how to instantiate objects using constructors; how constructors actually work in JavaScript internally; and much more.

Let's begin!

What are constructors?

A constructor in JavaScript is nothing more than a function i.e. there is no special syntax for a constructor that makes it a constructor.

This is in contrast to constructors in classical OOP languages such as Java, C++, Python etc. In these languages, a constructor is a special thing. Typically, it's part of a class definition, with a special syntax (in Python, it's given by the __init__() method, while in Java, it's given by the name of the class itself) and is usually used to initialize an instance during the instantiation process governed by the class itself.

However, in JavaScript, as stated before, there is no notion of a class, hence the idea that a constructor is part of a class becomes meaningless right away.

A constructor in JavaScript is thus technically different than a constructor in a classical OOP language, but theoretically it's quite the same thing:

Let's define it the best we could:

A constructor in JavaScript is a function that is meant to create, and optionally initialize an object.

One thing that's right away apparent from the discussion so far is that a constructor is a function. Likewise, we should expect all the concepts that apply to functions to apply to constructors as well.

Well, it turns out that almost all concepts native to functions in JavaScript are applicable to constructors as well, with the exception of only a few (we'll see these exceptional cases in this chapter).

Secondly, as the definition suggests, a constructor creates an object. Hence, when we are talking about a constructor, we are talking about some sort of object's creation, not the creation of any primitive.

Now, the definition given above for a constructor is a little incomplete. We've skipped on the idea of a prototype, because it's not been explained yet — including the notion of a prototype right now would only complicate things for you. In the next chapter, when we take a deep dive into prototypes, we'll review this definition in its complete form.

Anyways, coming back to understanding what a constructor really is, the ECMAScript 5.1 spec, section 4.2.1, itself defines constructors in a very beautiful way. It states that:

Objects in JavaScript may be created via constructors which create objects and then execute code that initializes all or part of them by assigning initial values to their properties.

Now for you, this might not be a 'very beautiful' description — it might sound even more alien than our original definition.

Well, if this is the case (which would most probably be), forget about everything in the definition after the phrase 'create objects'. That is, just try to understand the following: "Objects in JavaScript may be created via constructors which create objects...". This should confirm the fact that constructors are meant to create objects.

By the end of this chapter, we guarantee to you that you'll be able to make complete sense of this definition of a constructor put forward by the ECMAScript spec. Believe it!

Moving on, a constructor function in JavaScript is supposed to be invoked with the new keyword. This invocation with new is by far the most crucial thing in the world of JavaScript OOP.

In the most simplest words, when a constructor function is invoked with the new keyword, a new object is created — or in other words, constructed.

Calling a constructor function with new has the consequence of performing a couple of internal operations before invoking the actual function itself. These internal operations, as we shall see them later on in this chapter, serve to create an empty object and then call the constructor function with this object provided to it.

With the empty instance in hand, the constructor can add properties and methods to it, and thus, in this way, initialize it.

Note that it's possible to call a constructor function without new — after all it's a function. The same ECMAScript 5.1 spec beautifully states the consequence of doing so:

... Invoking a constructor without using new has consequences that depend on the constructor.

That is, it might just depend on the constructor about what would happen when new is omitted from the constructor's invocation expression.

Sometimes, it might be perfectly valid to call a constructor as a function (i.e. without the new keyword) while other times it won't.

For instance, as we have seen in the previous chapters, both the invocations new Array() and Array() are valid and produce the same result. In this case, it's apparent that the Array() constructor function doesn't impose any restriction on the context of its invocation — it could be called either as a constructor or an ordinary function.

Similarly, as stated before, it might not be valid to call a given constructor as a function. For instance, calling the IntersectionObserver() constructor as an ordinary function would throw an exception, asking the programmer to explicitly call the function with new.

Intersection observers were added into JavaScript to simplify the work of observing different HTML elements entering or exiting the viewport (in fact, much more than just that) to enable things such as ad analytics and many kinds of UI features.

It is an advanced concept that is detailed in our Advanced JavaScript course, in the unit JavaScript Intersection Observers — Introduction.

Conversely, just as it might be invalid to call a constructor as a function, sometimes it might be invalid to call a function as a constructor. For instance, the Symbol() function can't be called as new Symbol().

Symbols are an advanced concept in JavaScript and likewise we don't explore them in this course. In fact, you wouldn't need them at all for this course. But yes, they are ultimately a useful feature.

To learn more about symbols in JavaScript, refer to Advanced JavaScript — Symbols.

In the following sections, we'll see how to check whether a given function is called as a function or as a constructor.

Anyways, so this was a good amount of discussion on the very primitive definition of a constructor function in JavaScript. Hopefully, at this point you have a basic idea of what a constructor is (recap: it's a function that's meant to create objects).

In the next sections, we dive right into exploring even more about this constructor function theory of JavaScript, and finally create constructor functions of our own.

Constructors vs. ordinary functions

As stated before, there is absolutely no syntactic difference between creating a constructor function and creating an ordinary function in JavaScript. Both could be created using any of the ways we discussed in the chapter JavaScript Functions Basics.

So how do we figure out what's a constructor and what's not?

Well, what really distinguishes a constructor from an ordinary function is its body.

We've been seeing ordinary functions throughout this course, and in a much more higher frequency in the JavaScript Functions unit.

An ordinary function might log some data to the console; take some arguments and then maybe print their sum, or maybe add them to a global array; perform a whole procedure such as asking the user for their name, then uppercasing it, and finally passing it to alert(), which is guess what... another function; the list is just endless.

To go even far, an ordinary function might even create and return an object explicitly. We saw such functions in the JavaScript Functions Basics chapter, where we learnt that they're called factory functions, since they are factories that spawn new objects.

However, an ordinary function can't assert that it gets passed a new empty object out of thin air, and then initializes that object using the keyword this, and then implicitly returns that object.

That's just NOT how an ordinary function would ever be set up on Earth (we don't know about Mars, or any other planet 🤔).

A constructor function, on the otherhand, gets an object out of thin air assigned to this, then optionally initializes it, and finally implicitly returns that object.

We'll see what's this 'thin air' where a constructor gets its object from.

A constructor function could be left empty as well. Now note that if the constructor's body is empty, then there is simply nothing for us to see to judge whether the function is a constructor or not.

It turns out that in this case, the way we call the function and especially name it determines whether it's a constructor or not i.e. we call it with the new keyword and name it with an uppercase first letter (for e.g. Animal instead of animal).

Wait... Where does this uppercasing-the-first-letter convention come from? Why are constructors named this way?

Good question. It seems you're quite much following all of this discussion very carefully. Well, let's answer this question.

In JavaScript, a constructor function can be pretty much thought of as a class from the OOP world.

What is a class in object-oriented programming? At the core, it's something that defines characteristics and behavior of each of its instances.

That's quite what a constructor in JavaScript is. It defines characteristics and behavior of each the objects constructed from it — or better to say, instantiated from it. You won't be able understand this right now, but don't worry; it'll all become clear as you keep reading.

Now, since naming classes in OOP has the convention of uppercasing the first letter, we'll do the same for naming constructors in JavaScript.

Simple!

There is no class in JavaScript!

An important thing to keep in mind over here is that a constructor in JavaScript is only like a class from a pure class-based OOP language — it's NOTactually the same thing!

There are two categories of OOP languages: class-based and prototype-based. As mentioned before, Java, C++, Python, PHP fall in the first category i.e. class-based. JavaScript and the Self programming language fall in the second category i.e. prototype-based.

You'll often see people saying that a given object o belongs to the class A in JavaScript. The word 'class' here only conveniently implies that the constructor of that object is A(), not that there actually exists a class construct in JavaScript.

The word 'class' is used because its rudimentary to any OOP talk. Devs and other people find it much intuitive to say that an object belongs to some class A instead of saying that it is constructed using the constructor A().

In fact, in many OOP languages, the latter statement i.e. 'an object is constructed using the constructor A()', isn't quite right. In these languages, an object can never be created using a constructor — it's created using a class whose constructor gets called to simply initialize the object after it has been created.

Anyways, if we see it very carefully, saying that an object o belongs to the class A in JavaScript is technically not wrong.

A class in OOP defines characteristics and behavior related to each of its instances, right? That's exactly the case with the constructor function A(), as stated here.

In JavaScript, the constructor function is the 'thing' that defines characteristics and behavior of each of its instances (by virtue of being a constructor function which initializes its instance with various properties and methods, and by virtue of the prototype property on itself.)

In even simpler words, all of the traits of the given object o are nicely defined in A alone, not in any other place. Hence, it's indeed not wrong to say that A is the class of o.

Creating a constructor

Recall the point object we created in the previous to previous JavaScript Objects — Basics chapter, using the object literal syntax. It modeled a point on a 2D plane, and had two properties x and y corresponding to the x and y coordinates of the point, respectively.

A point on a graph is a really good starting point to experiment with constructor functions in JavaScript.

Let's create the same point object, this time using a constructor.

Before anything else, the first concern we ought to address is what name to give to the constructor.

Well, once again, we'll use the fact that a constructor in JavaScript is quite similar to the idea of a class from the OOP universe. Let's see how to use this fact...

We need to create point objects using our constructor, right? Each of these point objects could be said to belong to the class Point, couldn't it? In other words, each object is a Point.

Seems quite reasonable, doesn't it?

Hence, following from the fact that a point belongs to the class Point and that a constructor in JavaScript is analogous to the concept of a class, we'll call the constructor Point. Simple!

OK, so now let's finally define this constructor function Point():

JavaScript
function Point() {
   console.log('A new point created.');
}

For now, we just have a simple log in here. Later on, in this chapter, we'll be adding more stuff in the constructor's body to initialize the newly-created instance.

Anyways, let's go on and create a Point object.

Creating an object using a constructor is accomplished by invoking the constructor via new, as we already know.

A constructor's invocation expression (with new) creates and returns a new instance of the constructor. The overall process of creating the instance is called instantiation.

In the following code, we instantiate a Point object (in line 5) and then log it to see how it's represented in the console:

JavaScript
function Point() {
   console.log('A new point created.');
}

var p1 = new Point(); console.log(p1);
A new point created Point {}

In line 5, when the constructor is invoked (via new Point()), a handful of things happen.

We'll see all of these things in detail in the next section, but for now let's focus on the fact that amongst these things, the function Point() is actually called.

This means that its body is executed, which explains the first 'A new point created' log.

Once the constructor function completes, it returns the instance object created.

That is, the whole invocation expression new Point() gets replaced with the created instance object. In the code above, this object is stored in the variable p1. In line 6, we log this instance object.

The second log shows this instance object. It shows that the name of the constructor of the instance is displayed next to the instance's literal notation itself, which is empty in the case above.

Alright, all good uptil now!

We have a Point instance called p1. But wait... it doesn't have an x or y property.

What now?

Well, p1 is an object, right? And we studied in the previous chapters that objects could be assigned new properties at any time just by a simple assignment syntax together with bracket notation or dot notation. That's exactly what we could do here to add the properties x and y to our Point instance.

In the code below, we add the two properties x and y one-by-one to p1, set them to 0, and then finally log them:

JavaScript
function Point() {
   console.log('A new point created.');
}

var p1 = new Point();

p1.x = 0;
p1.y = 0;

console.log(p1);
A new point created Point { x: 0, y: 0 }

Great!

This is a constructor in action in JavaScript.

At this point, you might be thinking that creating a point object using a constructor function is quite more tiring compared to using an object literal.

Well, yes for now when we haven't used any of the potential of constructors, this whole code is definitely less concise than the one we saw in the chapter JavaScript Objects — Basics.

However, once we get to the essence of constructors, it would become apparent that they are way more powerful and capable than the typical and monotonous object literal syntax in cases where we have to create multiple objects all belonging to one particular class (from OOP terminology).

Let's see some of this potential.

Initializing the instance

Assuming that when a point is created, it's placed on the origin, i.e. (0, 0), we could configure our Point() constructor to do this automatically for us:

JavaScript
function Point() {
   this.x = 0;
   this.y = 0;
}

var p1 = new Point();

What's happening here is paramount to understand. Time to dive right in...

When Point() is called in line 6, as a constructor, it's quite obvious what's happening in its body i.e. the properties x and y are being assigned on the this object, but what's not that obvious is the value that this resolves to.

So what is the object that this resolves down to in the Point() constructor above?

If we know the answer to this, making intuition of this Point() constructor would be all peaches and cream — after all, it just adds two properties to that object.

It turns out that this holds the real potential of OOP in JavaScript:

  1. It's what makes it possible to initialize an object upon construction inside the constructor function.
  2. Furthermore, as we shall see in the next chapter, it's what provides a method's body, defined on the prototype, with the instance object calling it.

We already have a good working experience of this from the chapter JavaScript Objects — Basics. There, we learnt that this, when used inside a function, resolves to the object calling that function.

Yes, indeed that is true, but at least not in the case of a constructor's invocation.

When a constructor function is called (i.e. with new), internally a new empty object is created and then that object is assigned to this, which can then be accessed inside the function.

Hence, when we are assigning properties to this inside a constructor's body, we are, in effect, assigning properties to this new empty object constructed by the constructor's call.

Assigning properties to an instance from within the constructor is commonly known as initialization. We say that we are initializing the instance.

It's not valid to assign a value to this in JavaScript, such as this = {}. Only the internal engine could do this, and it actually does it all the time when entering the execution contexts of given functions.

Coming back to the code above, this means that this inside the constructor function Point() points to the Point object created internally by the call new Point().

Hence, this.x = 0 simply adds a property x to this Point object and sets it to 0, while this.y = 0 adds a property y and sets that to 0 as well.

Finally, as we know from the section above, this instance object is implicitly returned from the constructor function. In our case, this means that the return value of new Point() is merely an object with two props x and y, both set to 0.

Let's go on and instantiate two Point objects using this constructor and inspect them:

JavaScript
function Point() {
   this.x = 0;
   this.y = 0;
}

var p1 = new Point();
var p2 = new Point();

console.log(p1);
console.log(p2);
Point { x: 0, y: 0 } Point { x: 0, y: 0 }

As you can see, both the objects p1 and p2 are Point instances with x and y properties, both equal to 0.

Great!

Note that p1 and p2 defined here are two distinct objects. They have the same properties x and y and, at least for now, even the same values for those properties, but they are in no way identical to each other.

In other words, p1 === p2 would return false, as both the objects hold different references.

The idea of objects being stored as references in JavaScript is discussed in JavaScript Objects Basics — Passing by reference.

While describing the code above, it won't be wrong to say that a constructor is a template-kind-of thing that defines what a Point object looks like. We use this template to create two Point instances p1 and p2 in our code, each having their own x and y properties set to 0.

In other words, we don't have to manually go on and create each object using the same new literal syntax, merely copied and pasted again and again.

This doesn't only have the benefit of reducing repetition in rewriting an object literal for each new object, but also of allowing us to determine whether an arbitrary object is a Point instance or not (which we'll see how in a while). This definitely can't be achieved such easily and neatly with an object literal.

Guess what, this is just the beginning of exploring the potential of constructors in JavaScript — there's a lot more to follow.

Now before we explore more of that, let's go over the internals of a constructor's invocation in the next section i.e where does this gets its value from and what other hidden operations get performed behind-the-scenes while we instantiate an instance out of a constructor.

Internals of a constructor

We learnt in the chapter JavaScript Functions Basics that when a function is called, its internal [[Call]] method is invoked. Now, this only happens when the function is called in the context of a function, i.e. without the new keyword.

When a function is called in the context of a constructor, i.e. with the new keyword, the internal [[Construct]] method of the function object is called, if it exists.

So what happens inside [[Construct]]?

Well, it's quite easy to search for the algorithm executed by [[Construct]] in the latest ECMAScript spec, 12th edition at the time of this writing.

However, the thing is that the latest spec is quite big and complex, and with each algorithm assumes a lot of knowledge (really a lot!) about numerous concepts and internal function calls defined in the spec itself.

One has to be profound of all the primitive ideas discussed in the spec from its very beginning in order to be able to make any sense of most algorithms, like the one executed by the [[Construct]] internal method of functions.

At this stage, (or even at an advanced stage as a JavaScript developer, given that we're not an engine developer for the language) all this detail in the spec is of little value to us, at least at this point.

Older ECMAScript specs were better in this regard. They didn't assume a lot on the reader's end. Each algorithm was extremely easy to follow and make intuition of, even if the end reader didn't start off the spec from its very beginning.

Likewise, we'll refer to an old ECMAScript spec to understand what happens in [[Construct]]. Specifically, we'll be referring to the ECMAScript 5.1 spec.

Section 13.2.2, details the algorithm used in the [[Construct]] method of function objects.

We'll abstract away even more details from it that are not really useful for our discussion.

Given a function F, the expression new F() executes the internal [[Construct]] method on F that performs the following steps:

  1. A new empty object is created.
  2. The prototype of this object is set to F.prototype. We'll understand this step much better once we learn about prototypes in the next chapter.
  3. The [[Call]] internal method of the function is executed with its this set to the object created in step 1.
  4. The object created in step 1 is returned.

As you can see, the steps listed above are fairly easy to follow. Even if you were to inspect the actual algorithm as listed in Section 13.2.2 of the spec, you'll find that it's not difficult to make sense of either.

It's just the latest specs starting from the behemoth ES6 that really drive the mind crazy with tons and tons of detail.

Anyways, the most important steps to consider above are 1 and 3.

Step 1 states that calling new F() results in an empty object (an object with no own properties) being created behind-the-scenes.

Step 3 states that once this object is created (and its prototype set), the function is called just as any other normal JavaScript function is. That is, its internal [[Call]] method is invoked.

However, unlike an ordinary function, the this of a constructor is set to the newly created object. This allows the constructor function's body to initialize the newly-created object.

Practically, this model is an exceptionally intelligent one. A new object is created and then the respective function is executed normally as any other function, with its this configured to the newly-created object.

This is miraculous. We have an OOP principle being utilised, which is that of a constructor, yet what's powering it is a mere function, which is not an OOP idea.

Isn't this awesome?

Restating it once more, when a function is called as a constructor, its [[Call]] internal method is not invoked directly; rather, it's invoked from within the [[Construct]] internal method.

Now, knowing these internals won't make you a completely different JavaScript dev, but yes it would help you understand some other object-related behaviors in JavaScript much better (as we shall explore them in the next chapter) and make you much more confident about the knowledge you have for the language.

With the internals done, let's now resume with extracting more of that potential from our Point() constructor.

Constructor with parameters

In the last code snippet that we saw above, we defined a Point() constructor to create a point object set to the origin, with its x and y properties both assigned the value 0.

Now what if we want to create a point for some other x and/or y coordinates?

Well, one way is to go beyond and reassign the properties x and/or y.

This is illustrated below:

JavaScript
function Point() {
   this.x = 0;
   this.y = 0;
}

var p1 = new Point();

console.log(p1);

p1.x = 5;
p1.y = 10; console.log(p1);
Point { x: 0, y: 0 }
Point { x: 5, y: 10 }

This surely works. But it's redundant and tiring. That is, to change the point to some other set of coordinates or to create a point having different coordinates from the very beginning, we have to write two assignment expressions manually.

Well, the latter could be solved very easily by taking advantage of the fact that the constructor is just another function. So what, you ask?

Hmm. Think of it.

We could set up the Point() constructor to accept the x and y coordinates of the point to-be-created as arguments.

Consider the code below:

JavaScript
function Point(x, y) {
   this.x = x;
   this.y = y;
}

// Create a point on (5, 10)
var p1 = new Point(5, 10);

console.log(p1);
Point { x: 5, y: 10 }

With this Point() constructor in place, we could now very conveniently create a Point object to represent any point on a graph, not just a point on the origin, without having to manually type in the property assignment expressions.

Let's go on and actually create a couple of points:

new Point(50, 1)
Point { x: 50, y: 1 }
new Point(-2, -3)
Point { x: -2, y: -3 }
new Point(6, 0)
Point { x: 6, y: 0 }

Amazing!

Although this constructor works flawlessly, there's a slight betterment oppurtunity in it that we could, and preferably should, avail.

Let's see that...

Currently, if we want to create a bunch of points pointing to the origin, we have to manually pass in the arguments 0 and 0 to the constructor's invocation, i.e. new Point(0, 0). Given the fact that the origin is a common location, we could simplify this a bit.

That is, we could allow the arguments x and y to be omitted, and in that case assumed to be 0. In other words, we could make the parameters x and y optional and default them to 0, if omitted.

Let's do this using ES6 default-valued parameters:

JavaScript
function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;
}

var p1 = new Point();
console.log(p1);

var p2 = new Point(-2, 8);
console.log(p2);
Point { x: 0, y: 0 }
Point { x: -2, y: 8 }

Now our constructor is quite flexible. If we just write new Point(), that's effectively the same as new Point(0, 0).

ES6 default-valued parameters aren't supported, as with all ES6 features, on old browsers. Hence, to get the code above to work properly on these old browsers, we could simply use the ?:-style check.

Shown below is the code:

JavaScript
function Point(x, y) {
   this.x = x !== undefined ? x : 0;
   this.y = y !== undefined ? y : 0;
}

var p1 = new Point();
console.log(p1);

var p2 = new Point(-2, 8);
console.log(p2);
Point { x: 0, y: 0 }
Point { x: -2, y: 8 }

If we are 100% sure that the environment where our JavaScript program would run supports ES6, then without any second thought, we should go for ES6 default-valued parameters.

Otherwise, we should definitely use ?: or an if...else check, in order to support older browsers.

Adding methods to instances

So far, we are on the right track to building our Point() constructor from scratch with the desired functionalities for creating a point on a 2D plane.

When called without any arguments, it creates a Point object situated at the origin. Otherwise, it creates a Point object situated at the given set of coordinates (provided as arguments).

Simple enough.

This parameter version of our Point() constructor has surely kept us from having to manually assign values to the x and y properties of a Point instance after it is created, if we want to create a point located at some place other than the origin.

However, even with this parameterized Point() constructor, one thing is still not addressed. That is when we want to change the coordinates of a given point after it has been created.

For example, suppose that we create a new point object via the expression new Point(0, 2) and then save the returned instance in the variable p1. Now what if we want to change this point to (1, 5)?

Well, given the setup we have right now, there's only one way to do this, as shown below:

JavaScript
function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;
}

var p1 = new Point(-2, 8);

// Reposition p1 to (1, 5)
p1.x = 1;
p1.y = 5; console.log(p1);
Point { x: 1, y: 5 }

As can be seen, we have to manually change the respective properties one-by-one.

Once again, this manual work is tiring. The good news is that it could be prevented as well.

But how?

Well, recall the fact that inside a constructor, we initialize the instance being created by adding properties to it. Methods are simply special cases of properties so we could add them to the instance as well and thus describe its behavior.

In our case, we could define a method that serves to reposition a point to another set of coordinates. It would take in two args x and y, and then assign them to the x and y properties of the Point object, respectively.

Let's call this method setTo(). With this method, we would no longer need to manually write assignment statements to change the x and y properties of a given Point.

In the code below, we define this method:

JavaScript
function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.setTo = function(x, y) {
      this.x = x;
      this.y = y;
   }
}

Let's go on and use this method to do the same point repositioning that we did in the code above:

JavaScript
function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.setTo = function(x, y) {
      this.x = x;
      this.y = y;
   }
}

var p1 = new Point(-2, 8);

// Reposition p1 to (1, 5)
p1.setTo(1, 5);

console.log(p1);
Point { x: 1, y: 5, setTo: f }

As is clear, this time we don't have to go through the manual work of reassigning both the properties x and y on the object p1, separately. The repositioning is only one call away.

In the log here, we notice three properties, namely x, y and setTo; not two properties. This is important to understand. setTo is also a property at the end of the day — it's just a special case of a property, one that holds a function, called a method

Good!

Although this code works absolutely fine, there is now a completely different kind of a problem in it. This problem is discussed in the next section.

Problem with constructors

Any code that is written inside a constructor's body is executed for each instantiated object. In the case of mere properties, that anyways have to be maintained for each instance, this isn't a problem.

After all, each instance usually has distinct properties. But behavior is typically not local to instances.

For instance, there might be tons of different Tesla Model S cars on Earth right now, each with different properties, such as the name of the owner, the body color, etc, but they all have the same internal mechanism of braking, right?

In simple words, adding methods to instances from within the constructor in the way we add properties is usually not a good idea, since for each instance there is a separate method, yet every single one of them performs the same action.

Let's see this in more detail...

Consider the latest definition of Point() from the last code snippet shown above:

JavaScript
function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.setTo = function(x, y) {
      this.x = x;
      this.y = y;
   }
}

Let's create two instances p1 and p2 out of this Point() constructor and log the type of the setTo property of both these instances:

JavaScript
function Point(x = 0, y = 0) {
   /* ... */
}

var p1 = new Point(-2, 8);
var p2 = new Point();

console.log(typeof p1.setTo);
console.log(typeof p2.setTo);
function function

Quite trivially, as can be seen here, both instances p1 and p2 have a setTo property available that points to a function.

Now, let's go on and compare the setTo() method of p1 with that of p2 to see whether they both are the same:

p1.setTo === p2.setTo
false

No, they ain't the same!

p1 has its ownsetTo() method while p2 has its ownsetTo() method. Both these methods perform exactly the same task, yet they ain't the same.

This is the problem.

Our code is not DRY (Don't Repeat Yourself).

We are merely repeating stuff. Note that this repetition is not in terms of actual code — verbally, there isn't stuff that is being repeated (at least for the sake of argument). Rather, the repetition is in terms of the way the code is creating stuff in memory.

To be more precise, the code is creating a separatesetTo() method for each instance. This separate method for each instance comes at a cost:

  1. Storing these different function objects assigned to setTo obviously consumes memory.
  2. Storing keys on an object consumes memory as well. Here, we are referring to the key 'setTo'.
  3. Storing references (i.e. addresses) to function objects also takes up memory. Here, we are referring to the actual value stored inside a property.

In short, the code above is consuming a relatively large amount of memory without any purpose. To put it other way, the code above is simply wasting memory.

Obviously, for now (or practically, for most of the applications you'll make), that's not a significant amount of wastage.

But if we were to continue on with this memory-wasting coding practice to make intricately complex JavaScript applications, we would eventually end up with a performance bottleneck, due to high memory usage.

In complex applications, even the smallest of sensible optimizations matter.

So this is the problem with constructors:

Constructors, generally speaking, shouldn't be used to define instance methods.

One obvious remedy to this is to create the function objects in the global scope and then create methods on the instance by means of passing a reference to these global functions.

Let's try this with our code:

JavaScript
function pointSetTo(x, y) {
   this.x = x;
   this.y = y;   
}

function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.setTo = pointSetTo;
}

var p1 = new Point(-2, 8);
var p2 = new Point();

console.log(p1.setTo === p2.setTo);
true

We have taken the definition of the function assigned to setTo outside the Point() constructor. In this way, it's not re-created each time Point() is called. The log made also shows that both p1.setTo and p2.setTo point to the same function object.

In terms of memory consumption, this is definitely a better choice.

How?

Well, we've effectively cut down on the memory wastage that resulted from creating a new function object each time Point() was called. Now the function has been declared once in the global scope, and just being referred to from within Point().

But, nonetheless, this still doesn't solve all of our problems...

Firsly, we are still defining setTo on each instance, even though there is no need to. Ideally, stuff defined separately for each instance of a constructor should define traits of the instance (that are usually different for each instance) and in this way define its state. So that's problem number one.

Secondly, we have effectively lost encapsulation of the stuff related to a Point object within the Point() constructor. That is, we are now filling up the global scope with stuff that ideally should've been somehow defined as part of the Point class only, and not anywhere else.

Imagine if we had five such global functions to be ultimately assigned to properties of a Point instance inside the Point() constructor. Shown below is a very simple illustration:

JavaScript
function pointFunction1() { /* ... */ }

function pointFunction2() { /* ... */ }

function pointFunction3() { /* ... */ }

function pointFunction4() { /* ... */ }

function pointFunction5() { /* ... */ }


function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.method1 = pointFunction1;
   this.method2 = pointFunction2;
   this.method3 = pointFunction3;
   this.method4 = pointFunction4;
   this.method5 = pointFunction5;
}

This is quite a global mess, isn't it?

Plus, if the program defined some other global functions (in addition to these 5 functions) not related to the Point class in any way, then it would've been even more of a mess.

Now, someone could go on and argue that to prevent global namespace pollution, we could wrap an IIFE around the whole code defining the Point() constructor and its related functions.

This is possible, but much more headache than a solution. A solution ought to be one that solves a problem without imposing hundreds of problems of its own. Let's see the problems with using an IIFE here.

The code above, as encapsulated inside an IIFE, is demonstrated below:

JavaScript
var Point;

(function() {
   function pointFunction1() { /* ... */ }

   function pointFunction2() { /* ... */ }

   function pointFunction3() { /* ... */ }

   function pointFunction4() { /* ... */ }

   function pointFunction5() { /* ... */ }


   Point = function(x = 0, y = 0) {
      this.x = x;
      this.y = y;

      this.method1 = pointFunction1;
      this.method2 = pointFunction2;
      this.method3 = pointFunction3;
      this.method4 = pointFunction4;
      this.method5 = pointFunction5;
   }
})();

First of all, we have to make sure that Point is declared outside the IIFE (line 1) in order to be accessible in the global scope.

Secondly, we have to change the Point function declaration to a function expression (line 15) and assign that to the variable Point.

Moreover, we also have to increase the indentation of the code inside the IIFE.

But still, we are creating separate properties method1, method2, ..., on each Point instance, even though they all point to the exact same function objects internally.

Overall, the code looks extremely ugly and unmaintainable. We are creating IIFE overheads (putting them in memory, maintaining lexical environments, calling them) in our application completely unnecessarily.

If we look at the bigger picture from a distant view, everything is senseless here!

So an IIFE is clearly not a solution to our global-scope-pollution problem.

Another way could be to create an object literal holding all the functions that define behavior for a Point object:

JavaScript
var pointMethods = {

   function1: function() { /* ... */ },

   function2: function() { /* ... */ },

   function3: function() { /* ... */ },

   function4: function() { /* ... */ },

   function5: function() { /* ... */ }

}

function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.method1 = pointMethods.function1;
   this.method2 = pointMethods.function2;
   this.method3 = pointMethods.function3;
   this.method4 = pointMethods.function4;
   this.method5 = pointMethods.function5;
}

But once again, we are creating an extra object pointMethods here without any purpose at all, and shifting the logic of Points (i.e. the functions function1, function2, ...) out of the Point() constructor.

Moreover, as you can see, we are again and again referring to pointMethods in lines 19 - 23, which seems quite repetitive. Overall, the code looks even more uglier than the previous one.

So, using an object literal isn't a solution either.

Oh, wait! Let's put all the functions function1, function2, ..., on the Point function object itself. After all, it's an object as well.

This is demonstrated below:

JavaScript
function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.method1 = Point.function1;
   this.method2 = Point.function2;
   this.method3 = Point.function3;
   this.method4 = Point.function4;
   this.method5 = Point.function5;
}

Point.function1 = function() { /* ... */ };

Point.function2 = function() { /* ... */ };

Point.function3 = function() { /* ... */ };

Point.function4 = function() { /* ... */ };

Point.function5 = function() { /* ... */ };

Compared to creating a new object literal to store all the given function objects, this way is slightly better in that we don't have to introduce a new identifier into the environment where Point() exists, i.e. we don't have to create a separate object like pointMethods.

However, regardless of any of these ways — of using an IIFE, or an object literal, or the Point() constructor function itself to prevent global namespace pollution — we still haven't solved one long-lasting problem.

That is:

Each instance still has separatemethod1, method2, ..., method5 properties on itself that all together define behavior of the instance.

As stated before, behavior of instances are common to all instances and likewise methods (that define the behavior) must not be created for each instance separately.

To boil all of this discussion down, defining methods inside a constructor function's definition is, in almost all cases, NOT a good idea.

But, then what's the good idea? How to define methods for the instances of a class?

There's one and only one answer to this — prototypes. We shall discover them in the next chapter.

The instanceof operator

Recall that we said earlier in this chapter that creating objects using literals vs. creating them using constructor functions has the difference that we could easily distinguish between objects created using constructors.

It's now time to see how to do this...

The instanceof operator as the name suggests tells us whether a given object is an instance of a given constructor (which we could also think of as a class, remember?).

It's a binary operator as might already be apparent from its definition.

Syntactically, here's how to use it:

objinstanceofF

obj can be any valid JavaScript value while F must be a constructor function. This expression returns true if obj is an instance of F, or otherwise false.

Let's try running it on our Point objects p1 and p2:

JavaScript
function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.setTo = function(x, y) {
      this.x = x;
      this.y = y;
   };
}

var p1 = new Point(-2, 8);
var p2 = new Point();

console.log('p1 instanceof Point:', p1 instanceof Point);
console.log('p2 instanceof Point:', p2 instanceof Point);
p1 instanceof Point: true p2 instanceof Point: true

As can be seen, p1 and p2, as inspected using instanceof to be instantiated by Point(), both return true.

Let's test in the same way some arbitrary object:

{ x: 0, y: 0 } instanceof Point
false
{} instanceof Point
false

As expected, we get false returned in both cases, since { x: 0, y: 0 } is not a Point instance, and neither is {}.

Note that as stated before, instanceof returns true only when the given object is an instance of the given constructor, not when the given object is the given constructor itself.

That is Point instanceof Point won't return true:

Point instanceof Point
false

To test whether a given object is a given constructor, we could very simply compare it with that constructor using ===, quite obviously.

Point === Point
true

The constructor property

When an instance is created using a constructor, there is a constructor property available on the instance. This points to the constructor function used to create the object.

To mention it precisely, when an instance i is created from a given constructor F(), its prototype object is set to the prototype property of the constructor function F. This prototype object, that is common to all instances of F, contains a constructor property that points back to the constructor function.

Once again, we'll understand this much better once we are done with prototypes in the next chapter.

Anyways, let's bring into consideration our old Point() constructor function and an instance p1 constructed out of it:

JavaScript
function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;

   this.setTo = function(x, y) {
      this.x = x;
      this.y = y;
   };
}

var p1 = new Point(-2, 8);

We'll see what does p1.constructor have to return:

p1.constructor
ƒ Point(x = 0, y = 0) { ... }

As can be seen, p1.constructor returns back the constructor of p1, i.e. the Point() constructor.

This is exactly the same constructor Point(), likewise we could use it to create another instance, although it's not sensible to do so if we have direct access to the constructor.

An illustration is shown below:

new p1.constructor(2, 5)
Point { x: 2, y: 5, setTo: ƒ }
The constructor property is non-enumerable, i.e. it doesn't show up in a for...in loop. We learnt about property enumerability in the previous chapter JavaScript Objects — Property Attributes.

Constructor stealing

Back in the OOP Introduction chapter, we learnt that a subclass is a class that inherits from a given superclass. For instance, the Cat class is a subclass of the Animal class. Every Cat is an Animal.

In classical OOP languages, we could create classes that inherit from given classes. The constructor method in the subclasses could call the constructor methods of the superclasses.

In JavaScript, this idea of calling the parent class' constructor from within the constructor of the subclass is possible, but not as straightforward as it might seem.

Let's first see what's the problem in going the straightforward way.

Consider the task of creating a shape drawing application in JavaScript. At the forefront, this is a big challenge to undertake. It requires us to know about a ton of things — a ton literally means a 'ton'. Obviously, at this stage, we couldn't create this. But, at least, we could make some things to be ultimately used in the application.

Let's assume that each shape is denoted by the Shape class. A Shape could have a fill color given as a hexadecimal value (including the hash symbol), and a position on the canvas (the main drawing area) given as two properties x and y representing the x and y coordinates on the canvas, respectively (in addition to numerous other properties).

The most abstracted Shape constructor would look something like this:

JavaScript
function Shape(fill, x, y) {
   this.fill = fill;
   this.x = x;
   this.y = y;
}

On its own, the Shape class doesn't seem very useful. And, indeed, it isn't. It doesn't define the geometry of the underlying shape.

Since there can be multiple kinds of shapes, such as rectangles, circles, triangles, polygons, stars, and so on, and each of these would have a different geometry, it seems sensible to create subclasses of Shape to represent these shapes in the application.

The subclass we'll construct right now is Rect to denote rectangles.

Rect could have a length property and a height property to determine the area spanned by the rectangle.

Let's create this Rect class as well:

JavaScript
function Rect(length, height) {
   this.length = length;
   this.height = height;
}

Now, as we said before, Rect is a subclass of Shape. This simply means that every Rect is a Shape as well. Consequently, every Rect object must have all the properties a Shape object has.

Let's go on and define those properties in the Rect() constructor:

JavaScript
function Rect(length, height, fill, x, y) {
   // Initialize Shape properties
   this.fill = fill;this.x = x;this.y = y;

   this.length = length;
   this.height = height;
}

Note that we had to modify the parameter list of Rect here as well — previously, it had two parameters; now it has five of them.

But hey, wait. Isn't this repetition of code? We are redefining assignment statements for a Shape object again inside the Rect() constructor.

Yes, you guessed it right.

It sure is repetition of code and so we should avoid it. We could do much better than this.

One straightforward solution that might come in the mind of the novice developer (this is not you because you are quite much aware of many aspects of JavaScript at this point in this course) is to call the Shape() constructor from within the Rect() constructor as new Shape().

The thinking of calling Shape() is perfectly alright — after all, to prevent repeating the code written inside Shape(), we ought to call it. However, this invocation couldn't be a straightforward invocation with new.

Let's see the problem...

Below we call the Shape() constructor from within Rect() in line 3, passing it all the desired arguments:

JavaScript
function Rect(length, height, fill, x, y) {
   // Initialize this Shape
   new Shape(fill, x, y);

   this.length = length;
   this.height = height;
}

Time to test this code. First, we'll instantiate a Rect instance by providing some dummy arguments to Rect() and then log each of the five properties of this instance one-by-one:

var r1 = new Rect(20, 10, '#000', 0, 0)
undefined
r1.length
20
r1.height
10
r1.fill
undefined
r1.x
undefined
r1.y
undefined

Can you see the problem here?

The object doesn't seem to have any of the properties x, y or fill. length and height, though, are present. One thing is clear — there is some logical error in the code above.

Now before we go on and debug that error, take a break for a few seconds and think about the reason of this logical problem.

Why doesn't the r1 object have properties of a Shape?
The answer has to do something with functions.

Alright, it's answer time...

In the code above, when we call new Shape(), a new object is created and the properties fill, x and y added to that object. This is just the problem.

The Shape() constructor's invocation as a constructor simply creates a new binding for this, that is a new Shape instance. This instance is what receives all the initialization code of Shape().

Even if we were to assign Shape to this.parent inside Rect() and then go like new this.parent(), there would be absolutely no difference in the final result — the properties fill, x and y would be undefined on the Rect instance.

This can be seen as follows:

JavaScript
function Rect(length, height, fill, x, y) {
   // Initialize this Shape
   this.parent = Shape;
   new this.parent(fill, x, y);

   this.length = length;
   this.height = height;
}

That's because a constructor's invocation creates its own binding for this, regardless of the calling object (in this case, the object this inside Rect()). In layman terms, a constructor call hijacks the function's normal this binding.

To read more about the normal binding of this inside a function, refer to JavaScript Functions — this.

Any solutions?

Well, we have basically two of them:

One is already shown above, though in a slightly different way. It is to define a method on the instance inside Rect() and point it to the Shape() constructor, and then call this method, without using new.

This would automatically set this of the Shape() function to the calling object, i.e. the Rect instance.

Shown below is a demonstration:

JavaScript
function Rect(length, height, fill, x, y) {
   // Initialize this Shape
this.parent = Shape;
this.parent(fill, x, y); this.length = length; this.height = height; }

As before, let's test this:

var r1 = new Rect(20, 10, '#000', 0, 0)
undefined
r1.length
20
r1.height
10
r1.fill
'#000'
r1.x
0
r1.y
0

Yeah, it works!

The other way is to use the power of the function method call() to configure this for the constructor function.

This is much better than the previous way, as we don't have to create a new property (parent in the case above) on the instance to hold the parent constructor.

Remember how to use the call() function method? We covered it in the chapter JavaScript Function Methods — call().

The first argument to call() specifies the value to be used as this in the calling function; the rest of the arguments to call() specify arguments to be passed into the calling function.

Therefore, in our case, first of all we need to pass in this of the Rect() constructor to the invocation Shape.call() and then pass in the arguments x, y and fill.

Simple?

Let's implement this:

JavaScript
function Rect(length, height, fill, x, y) {
   // Initialize this Shape
Shape.call(this, fill, x, y); this.length = length; this.height = height; }

Time for the testing:

var r1 = new Rect(20, 10, '#000', 0, 0)
undefined
r1.length
20
r1.height
10
r1.fill
'#000'
r1.x
0
r1.y
0

Amazing!

You'll find this second way used very frequently out there — this is because it's the simplest way to emulate parent class constructor invocation in JavaScript.

It's good to be aware of the first way in case you ever encounter a script that uses it and not the call() function method.

So, at this point, we have successfully prevented repetition of the code within the parent constructor Shape() by reusing it inside Rect().

This technique is commonly referred to as constructor stealing, or constructor borrowing, following from the fact that we merely steal the constructor's code to be used in the context of another constructor.

Static methods

In the OOP world, given a property/method, it could either be an instance property/method or a static property/method.

Instance property/method is one that is directly accessible on an instance.

In contrast to this, a static property/method is one that is directly accessible on the underlying class — there's no way to access it directly via an instance of the class.

Static properties/methods are used when some characteristics/behavior ought to be defined that aren't related to instances, but rather related to the class.

For example, the Array class defines a static method called isArray(). That is, the method is accessible as Array.isArray(). This static method helps us determine whether a given value is an array or not.

The reason why it's static is because it isn't meant to be run on an Array instance (logically that would've been senseless, as if isArray() was an instance method, then it would've always returned true).

The method isArray() is meant to take any arbitrary argument and determine if that's an Array or not.

Note that this method Array.isArray() could've been provided as a global function (directly accessible on window as window.isArray() or simply as isArray()).

The benefit of adding it to the Array class is that the global namespace doesn't get overcrowded, and all array-related functionality sits nicely within the Array class (where it should ideally be).

To even better understand this concept, let's add a static method to our Point class that determines the distance between two points each given as an array of two elements, specifying the x and y coordinates of the point, respectively.

The most important thing to note here is that by the static method, we don't enforce the points to be represented as Point objects. They could be two-element arrays, or any other sequence whose first and second items give us the x and y coordinates of the underlying point.

Consider the following code:

JavaScript
function Point(x = 0, y = 0) {
   /* ... */
}
            
Point.distance = function(p1, p2) {
   // Calculate distance between p1 and p2.
   return Math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2);
}


var p1 = [0, 0];
var p2 = [3, 4];

console.log(Point.distance(p1, p2));
5

See how we are entertaining two arrays representing points and not enforcing them to be Point objects first.

Stating it once again, this distance() method on Point could've been a distance() function in the global scope. The benefit of using Point is that it prevents pollution of the global namespace and nicely keeps everything related to points within Point.

Great!

Calling constructors as functions

In the previous chapters, we've seen it numerous times that a predefined constructor is called as a function, yet it works exactly like a constructor.

For instance, if we call Array() or new Array(), we get exactly the same thing returned back.

How could this be accomplished for our Point() constructor?

Well, if you think about it, it's quite simple: if the constructor is invoked as a function, we reinvoke it as a constructor (i.e. using new). Otherwise, it means that it was invoked as a constructor and so, likewise, we proceed with the initialization code for the instance created.

But how do we check whether a constructor is called as a normal function or vice versa?

Hmm. This is a good question.

There are mainly two ways to accomplish this:

  1. Use the ES6-style expression new.target
  2. Use the instanceof operator

We start by considering the latter.

We'll take the example of our very own constructor Point(). First let's reason about how could instanceof be used.

If Point() is called as a constructor, what would its this resolve to? We just learnt about this in the sections above. It would resolve to the Point instance created.

On the otherhand, if Point() is called as a function or as a method then what would this resolve to? Well, if it's called as a function, this would clearly resolve to the global object window. Otherwise, if it's called as a method, this would resolve to the calling object.

This discussion has already laid the solution. Let's see it...

The thing is that we could use instanceof to check whether this inside Point() is an instance of the Point constructor. If this returns true, this means that we are on the right track, i.e. Point() was called as a constructor function (with new).

Otherwise, it means that Point() was called as a normal function/method (i.e. in a non-constructor context).

Easy? Let's finally implement this.

In the code below, we throw an error if the Point constructor is called as a function, or else proceed with the initializations of the instance.

JavaScript
function Point(x = 0, y = 0) {
   // If called as a function, throw an error.
   if (!(this instanceof Point)) {
      throw new Error('Illegal function call Point().');
   }

   // Otherwise, proceed as usual.
   this.x = x;
   this.y = y;

   this.setTo = function(x, y) {
      this.x = x;
      this.y = y;
   }
}

Let's try this:

new Point(2, 3)
Point { x: 2, y: 3, setTo: ƒ }
Point(2, 3)
Uncaught Error: Illegal function call Point(). at Point (<anonymous>:4:13) at <anonymous>:1:1

As you can see, the second prompt here throws an error, since it calls Point() without new.

Note that instead of throwing an error, we could also configure Point() to make the correct call itself. That is, when we call Point() as a function, the function could call itself as new Point().

This is accomplished below:

JavaScript
function Point(x = 0, y = 0) {
   // If called as a function,
   // recall as a constructor and return the instance.
   if (!(this instanceof Point)) {
      return new Point(x, y);
   }

   // Otherwise, proceed as usual.
   this.x = x;
   this.y = y;

   this.setTo = function(x, y) {
      this.x = x;
      this.y = y;
   }
}

Let's try this the same way we tried the constructor above:

new Point(2, 3)
Point { x: 2, y: 3, setTo: ƒ }
Point(2, 3)
Point { x: 2, y: 3, setTo: ƒ }

Now, Point() vs. new Point() holds absolutely no distinction in terms of the final return value.

Spread the word

Think that the content was awesome? Share it with your friends!

Join the community

Can't understand something related to the content? Get help from the community.

Open Discord

 Go to home Explore more courses