DEV Community

Cover image for An Easy Guide To Understanding Constructors In JavaScript
Lawrence Eagles
Lawrence Eagles

Posted on • Edited on

An Easy Guide To Understanding Constructors In JavaScript

Table of Contents

  1. An Introduction To Constructors
  2. Functions, Constructors And The new Operator
  3. Constructors And Prototypal Inheritance
  4. JavaScript's Built-In Constructors
  5. Closing Thoughts

1. An Introduction To Constructors

In the previous article in this series, we looked at prototypal inheritance in JavaScript and talked about important object-oriented (OOP) JavaScript concepts like the prototype, the prototype chain, inheritance, and more. We also looked at how to set an object's prototype using its __proto__ property (we noted that this is not the recommended way.) and dealt with the this variable in detail.
You can read through this article below:

In this article, we would pick up where we left off by looking at the recommended ways to set an object's prototype in JavaScript.
While there are several ways to do this, our focus here is on function constructors.

We will deal with the other recommended methods in the upcoming articles in this series. Talking in detail about all of them; in a single article would make it very long and grueling to read!

Constructors

Constructors in JavaScript are special functions that are used to construct objects. This topic may appear difficult and intimidating but it is actually very simple.

💡 The key to understanding constructors is to know that they are actually normal JavaScript functions. What makes them special is that they are always invoked along with a very powerful operator in JavaScript called the new operator.

Kindly run the code below and consider its result.

function Person () { this.firstname = "Lawrence" this.lastname = "Eagles" this.occupation = "Software Developer" this.gender = "male" } const Lawrence = new Person(); console.log(Lawrence);

Our small contrieved example above creates a new object and stores a reference to it in the Lawrence variable. This object has all the properties specified in the Person constructor.
The Person function itself is a normal JavaScript function; what gives it the power of a constructor (the power to construct objects) is this line of code:

const Lawrence = new Person();  
Enter fullscreen mode Exit fullscreen mode

✨ The new operator modifies the behaviour of the function it operates on. Using this along with some JavaScript design patterns, we are able to create powerful constructors. We will elaborate on this in the next section.

2. Functions, Constructors And The new Operator

In section 1 we learned that when the Person constructor, (or any other constructor) is invoked without the new operator it is invoked as a regular JavaScript function. In this section, we will elaborate on this with code examples.
Kindly consider the code below.

function Person () {
     this.firstname = "Lawrence"
     this.lastname = "Eagles"
     this.occupation = "Software Developer"
     this.gender = "male"
}
Enter fullscreen mode Exit fullscreen mode

Above is the declaration of the Person function. We can notice two things from it viz:

  • It sets some properties e.g firstname, lastname, occupation, and gender to the object the this variable binds (or is pointing) to. In this case the global object.

💡 As mentioned in the previous article in this series, one rule of the this variable is that when it is used inside a function it points to the global object but when it is used inside a method (a function in an object) it would point to that object

If this is not very clear to you, feel free to visit my previous article on OOP JavaScript. I have already provided a link to it in section 1.
However, here is a quick recap.
Kindly run the code below and consider its result

const devName = "Lawrence Eagles" function tellDevName () { console.log("result", this.devName) } tellDevName(); // returns "Lawrence Eagles"

The above example shows that the this variable inside a function is pointing to the global object.

  • Another thing that should be pretty obvious about the Person function is that it does not have a return statement hence when invoked it would return undefined.

💡 The JavaScript engine would return undefined from any function that does not return a value. This behaviour is leveraged in creating constructors as we are able to modify what that function returns using the new operator. Hence it is a common pattern in JavaScript, that constructors do not have a return statement

The New Operator

This is a very powerful JavaScript operator that has the ability to modify certain behaviours of a function.
The new operator can be very confusing and somewhat intimidating at first.

The key to understanding it is to always see it as another regular JavaScript operator. Hence, a good understanding of operators in JavaScript is necessary to grasp the power of this operator.

Operators

Operators are special JavaScript functions that are syntactically different from regular functions. They are not like a regular JavaScript functions objects hence passing them to console.dir() would throw an error. You can see some code examples below.
Kindly run the codes below and consider the results:

function tellDevName () { console.log("result", this.devName) } console.dir("function properties", tellDevName) console.dir("function properties", Date) // if you uncomment the lines below and run the codes in you get an error. // console.dir("function properties", +) // console.dir("function properties", new)

💡 The console.dir() displays an interactive list of the properties of the specified JavaScript object

codes result from runkit

You can see all the properties of the tellDevName function and the Date constructor when you run the code but if you uncomment the lines where I passed an operator as a parameter, and try to run the code, runkit would throw an error, this tells us that they are not regular function objects.

Operators much like regular functions take parameters (which are called operands) but unlike regular functions, they give us a convenient syntax which can be in the form of any of the three notations below:

  • Infix Notation: In this notation, operators are placed between their operands. Kindly consider the code below:
2 + 2 // returns 4
3 * 3 // returns 9
4 - 4 // returns 0
5 / 5 // returns 1
6 % 2 // returns 0
Enter fullscreen mode Exit fullscreen mode

In our examples above each operator sits between two parameters (operands) and returns a value. Learn more about the infix notation here

  • Postfix Notation: In this Notation, the operators follow their operands. Kindly consider the codes below:
const mixNumbers = [1,2,3,4,5,6,7,8,9,10,11,12]
const evenNumbers = []
for (let i=0; i < mixNumbers.length; i++) {
    if (mixNumbers[i] % 2 === 0){
       evenNumbers.push(mixNumbers[i])
    }
}
console.log("even numbers", evenNumbers)
Enter fullscreen mode Exit fullscreen mode

Above is a small example that finds the even number from a list of numbers. But what concerns us from this example is the increment operator.
There is also the decrement operator. Learn more about the postfix notation
Kindly consider the code below:

i++ increment operator
i-- decrement operator
Enter fullscreen mode Exit fullscreen mode
!true               // logical NOT (!) returns false
!false              // logical NOT (!) returns true
++i                 // prefix increment             
--i                 // prefix decrement
new constructor()   // returns the newly constructed object
Enter fullscreen mode Exit fullscreen mode

From our examples above we can see that the new operator uses the prefix notation it takes a function (constructor) invocation and returns a newly constructed object.

🎉 I do hope that our succinct discourse on operators, would make you understand the new operator better and hence further your understanding of function constructors

With our understanding of operators, we can now clearly see that the new operator actually takes a function (constructor) invocation as its parameter(operand) it then performs some operations on it and returns a value.
Below are the operations of the new operator on a function constructor.

  • Creates an empty object and binds (points) the this variable to the newly created object.
  • Returns the object the this variable binds to (the newly created object) if the function doesn't return its own object (this is why constructors should not have a return statement). Kindly run the codes below and consider the results:
// regular function function person () {} const regularDeveloper = person() console.log("regular function result", regularDeveloper) // constructor function Person () { console.log("this binds to", this) } const constructorDeveloper = new Person() console.log("Person constructor result", constructorDeveloper) function BadPerson () { console.log("this binds to", this) return {name: "Jack", age: "70"} } const BadJack = new BadPerson() console.log("BadPerson constructor result", BadJack)

From the code example above I have deliberately given 2 of the 3 functions the same name but since JavaScript is case sensitive they are two different functions. Notice that the first letter of the constructor's name is capitalized while the regular function name is all lowercase.

💡 We use this pattern in JavaScript to differentiate constructors from regular functions. The first letter of a constructor's name is always capitalized. This would also serve as a reminder for you to use the new operator with every constructor. In our code example above, Person is the constructor and person is the regular function.

We can see from the result of the code above that the regular function returns undefined as expected but the constructor returns a new object created by the new operator which also binds the this variable in that constructor to this object.

💥 Also notice the BadPerson constructor that returns its own object fails to return the object created from the new operator. This is a pitiful we must avoid. Again, as a rule, a constructor should not have a return statement❗

Alt JavaScript constructor's result

JavaScript Design Patterns For Creating Constructors

With our knowledge of constructors and the new operator, we can easily add properties to the newly constructed object . Here is a common JavaScript pattern for this.
Kindly consider the code below

function Person () {
     this.firstname = "Lawrence"
     this.lastname = "Eagles"
     this.occupation = "Software Developer"
     this.gender = "male"
}
Enter fullscreen mode Exit fullscreen mode

The only limitation here is that any object created by this constructor is always going to have these properties. In other to make the object properties dynamic, we can pass them as parameters to the constructor (since constructors are regular functions in the first place).
Kindly run the codes below and consider its result:

function Person (firstname, lastname, occupation, gender) { this.firstname = firstname this.lastname = lastname this.occupation = occupation this.gender = gender } const Developer = new Person("Lawrence", "Eagles", "Software Developer", "Male") const Doctor = new Person("Ben", "Carson", "Neurosurgeon", "Male") const Scientist = new Person("Albert", "Einstein", "Scientist", "Male") console.log("developer", Developer) console.log("doctor", Doctor) console.log("scientist", Scientist)

From the results of running the code above, we can see that the arguments passed to each constructor, when invoked with the new operator are used to set up the properties of the newly constructed objects.
You can read more about the new operator at MDN.

  • Lastly the new operator links (sets) the prototype of the newly created object to another object. In our introduction, we said we were going to talk about the recommended ways to set an object's prototype and our focus was on function constructors. This point brings our long discourse back to the subject matter. Let's talk more about it in the next section.

3. Constructors And Prototypal Inheritance

In JavaScript, every function has a property called the prototype. This sits as an empty object in the function and remains dormant throughout the life of that function. It would only become active and quite useful if that function is used as a constructor.

💡 The prototype property of a function (which is an object in JavaScript) is not the prototype of that function object but it acts as the prototype of any object constructed with that function when it is used as a constructor. The naming is a little confusing but we love our JavaScript 😉

Kindly run the code below and consider its result:

function Person (firstname, lastname, occupation, gender) { this.firstname = firstname this.lastname = lastname this.occupation = occupation this.gender = gender } // lets add some properties to the prototype property of the Person constructor. Person.prototype.getPersonBio = function () { console.log("Hello my name is " + this.lastname + " " + this.firstname + " I am a " + this.occupation ) } const Developer = new Person("Lawrence", "Eagles", "Software Developer", "Male") const Doctor = new Person("Ben", "Carson", "Neurosurgeon", "Male") const Scientist = new Person("Albert", "Einstein", "Scientist", "Male") console.log("Developer's bio:", Developer.getPersonBio()) console.log("Doctor's bio:", Doctor.getPersonBio()) console.log("Scientist's bio", Scientist.getPersonBio())

From the results of the code above we can see that all the objects constructed with the Person constructor have access to the getPersonbio method which sits in the prototype property of the Person constructor. As we have noted above this property becomes the prototype of each object.

The details of how the search is made down the prototype chain in other for all the constructed object to access the getPersonBio method has already been discussed in the previous article. I would kindly suggest you take a look at it if this section is not very clear to you.

4. JavaScript's Built-In Constructors

JavaScript comes with some built-in constructors. If you are a JavaScript developer, there is a high probability that you have used some of them.
Kindly run the code below and consider its result:

const NumObject = new Number("20") const StringObject = new String("Hello World") const Today = new Date() console.log(NumObject) console.log(StringObject) console.log(Today)

From running the codes above we can see that each returns an object because every constructor in JavaScript returns an object.
You can learn more about each of these built-in constructors from the links below:
Number Constructor
String Constructor
Date Constructor

These are just a selected few you can get a more robots list here

5. Closing Thoughts

I do hope you followed through to this point. If you did you are really appreciated. It has been a long discussion, and I hope you got a thing or two. If so, I would now be looking forward to hearing your opinions, comments, questions, or requests (in case anything is not clear) in the comments section below.

Top comments (2)

Collapse
 
jisokoo profile image
jisokoo

Hello,
First thanks a lot for these useful article, really help me understand the concepts.
Just a little thing I can't figure out: in the example of section 3. why does the results log the following:
"Hello my name is Eagles Lawrence I am a Software Developer"
"Developer's bio:"
undefined

I understand the first line but why is "Developer's bio:" after sentence "Hello my name is ..." ? And why does it returns undefined after?
Hope you can help me see this clearer
Thanks

Collapse
 
b2gogoi profile image
Bhaskar Gogoi

Its because the function(getPersonBio) just has a console.log which gets executed first and not returning anything so undefined later