Javascript

Javascript Constructor Functions and The “this” Keyword

Javascript Constructor Functions and The this Keyword

You may have heard before about constructors in object oriented languages such as C++ and Java. The same concept also exist in javascript but with a different methodology.

 

 

Constructors are usually used in the creation of objects. In languages such as C++ in order to create objects you have to create a user defined class. However in javascript to create an object you have to create a constructor function. Note that in Ecmascript 6 there is a modern way of creating objects using classes but it’s not fully supported yet a cross web browsers, so in this article we will talk about constructor functions.

A constructor function is a simple javascript function that is used to create objects using the “new” keyword. So instead of calling the function directly you have to invoke it with “new” keyword. 

Consider this example:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function Rectangle()
{
this.width = 70;
this.height = 40;
console.log("new object created!");
}
function Rectangle() { this.width = 70; this.height = 40; console.log("new object created!"); }
function Rectangle()
{
    this.width = 70;
    this.height = 40;

    console.log("new object created!");
}

Now to create an object from Rectangle  we have to use the new keyword:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var rectangle = new Rectangle();
console.log(rectangle);
var rectangle = new Rectangle(); console.log(rectangle);
var rectangle = new Rectangle();
console.log(rectangle);

Now check the console you will see this output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
new object created
Object { width: 30, height: 40 } // object
new object created Object { width: 30, height: 40 } // object
new object created
Object { width: 30, height: 40 }   // object

As you see in the output that the rectangle type is an object not Function. Also you can view the object constructor using javascript constructor property like so:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(rectangle.constructor);
// output
function Rectangle()
console.log(rectangle.constructor); // output function Rectangle()
console.log(rectangle.constructor);

// output
function Rectangle()

But how this is works? The secret behind that is the “this” keyword in the above function. “this” is a special keyword in javascript like var, let, const etc. “this” refers to the current object when a new object is created. So “this” is useless until a new object instantiated.

So in the above example when i create a new object like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var rectangle = new Rectangle();
var rectangle = new Rectangle();
var rectangle = new Rectangle();

Now “this” refers to the rectangle object, therefore using rectangle object i can access any property inside the function like so:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(rectangle.width);
console.log(rectangle.height);
console.log(rectangle.width); console.log(rectangle.height);
console.log(rectangle.width);
console.log(rectangle.height);

 

Another advantage of constructor functions is that you can create as many objects as you can, each object has it’s own state:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var rectangle2 = new Rectangle();
rectangle2.width = 50;
rectangle2.height = 20;
console.log(rectangle2.width, rectangle2.height);
var rectangle3 = new Rectangle();
rectangle3.width = 200;
rectangle3.height = 80;
console.log(rectangle3.width, rectangle3.height);
var rectangle2 = new Rectangle(); rectangle2.width = 50; rectangle2.height = 20; console.log(rectangle2.width, rectangle2.height); var rectangle3 = new Rectangle(); rectangle3.width = 200; rectangle3.height = 80; console.log(rectangle3.width, rectangle3.height);
var rectangle2 = new Rectangle();
rectangle2.width = 50;
rectangle2.height = 20;
console.log(rectangle2.width, rectangle2.height);
        
var rectangle3 = new Rectangle();
rectangle3.width = 200;
rectangle3.height = 80;
console.log(rectangle3.width, rectangle3.height);

 

Constructor Function Parameters

As with normal javascript functions you can pass parameters to constructors as well, so modify the above example to pass the width and height of rectangle:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function Rectangle(width, height)
{
this.width = width;
this.height = height;
console.log("new object created");
}
let rectangle1 = new Rectangle(70, 20);
console.log("Rectangle 1: " , rectangle1.width, rectangle1.height);
let rectangle2 = new Rectangle(300, 50);
console.log("Rectangle 2: " , rectangle2.width, rectangle2.height);
function Rectangle(width, height) { this.width = width; this.height = height; console.log("new object created"); } let rectangle1 = new Rectangle(70, 20); console.log("Rectangle 1: " , rectangle1.width, rectangle1.height); let rectangle2 = new Rectangle(300, 50); console.log("Rectangle 2: " , rectangle2.width, rectangle2.height);
function Rectangle(width, height) 
{ 
    this.width = width; 
    this.height = height; 
             
    console.log("new object created"); 
}
        
let rectangle1 = new Rectangle(70, 20);
console.log("Rectangle 1: " , rectangle1.width, rectangle1.height);
        
let rectangle2 = new Rectangle(300, 50);
console.log("Rectangle 2: " , rectangle2.width, rectangle2.height);
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// output
new object created
Rectangle 1: 70 20
new object created
Rectangle 2: 300 50
// output new object created Rectangle 1: 70 20 new object created Rectangle 2: 300 50
// output
new object created
Rectangle 1:  70 20
new object created
Rectangle 2:  300 50

Also you can pass a parameter that’s is also a parameter created from a constructor function.

For example in the above example if we want to add another argument that specify the rectangle style like background, border type, etc, we can make another constructor function like this one:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function RectStyle(background, borderStyle)
{
this.background = background;
this.borderStyle = borderStyle;
}
function RectStyle(background, borderStyle) { this.background = background; this.borderStyle = borderStyle; }
function RectStyle(background, borderStyle)
{
    this.background = background;
    this.borderStyle = borderStyle;
}

Then create a new object from this function:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var rectangleStyle = new RectStyle("red", "dashed");
var rectangleStyle = new RectStyle("red", "dashed");
var rectangleStyle = new RectStyle("red", "dashed");

Then pass this argument as a third argument to Rectangle:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function Rectangle(width, height, rectStyle)
{
this.width = width;
this.height = height;
this.rectStyle = rectStyle;
console.log("new object created");
}
function Rectangle(width, height, rectStyle) { this.width = width; this.height = height; this.rectStyle = rectStyle; console.log("new object created"); }
function Rectangle(width, height, rectStyle) 
{ 
     this.width = width; 
     this.height = height; 
     this.rectStyle = rectStyle;
     console.log("new object created");  		 
}

Now when creating a new instance of Rectangle pass the third argument “rectangleStyle” as shown:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let rectangle1 = new Rectangle(70, 20, rectangleStyle);
let rectangle1 = new Rectangle(70, 20, rectangleStyle);
let rectangle1 = new Rectangle(70, 20, rectangleStyle);

As this point you can access any rectangle style property from rectangle1:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(rectangle1.rectStyle);
console.log(rectangle1.rectStyle.background); // red
console.log(rectangle1.rectStyle.borderStyle); // dashed
console.log(rectangle1.rectStyle); console.log(rectangle1.rectStyle.background); // red console.log(rectangle1.rectStyle.borderStyle); // dashed
console.log(rectangle1.rectStyle);
console.log(rectangle1.rectStyle.background);   // red
console.log(rectangle1.rectStyle.borderStyle);   // dashed

 

Built in Constructors In Javascript

Javascript have many built-in constructor functions for each primitive types like String, Number, Boolean, Object, etc.

For example you can declare a string like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const str = new String("hello world");
console.log(str); // Object String {'hello'}
const str = new String("hello world"); console.log(str); // Object String {'hello'}
const str = new String("hello world");

console.log(str);   // Object String {'hello'}

Also you can declare numbers:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const num = new Number(200.56);
console.log(num); // Number {200.56}
const num = new Number(200.56); console.log(num); // Number {200.56}
const num = new Number(200.56);
        
console.log(num);  // Number {200.56}

But be careful don’t use this approach as it slows down the program, instead use the primitive types directly.

 

Declaring Methods Inside Constructors

Like declaring properties inside constructor functions we can declare methods also, using the “this” keyword and those methods is accessible outside when a new object is created.

Imagine in the above example that we want to add a method to draw the rectangle on the browser window:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<canvas width="600" height="600" id="my_canvas"></canvas>
<script>
function Rectangle(x, y, width, height, color)
{
this.width = width;
this.height = height;
this.draw = function () {
var canvas = document.getElementById('my_canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.fillStyle = color;
ctx.clearRect(x, y, width, height);
ctx.fillRect(x, y, width, height);
}
}
}
let rectangle1 = new Rectangle(30, 50, 150, 50, 'green');
rectangle1.draw();
let rectangle2 = new Rectangle(300, 200, 300, 50, 'red');
rectangle2.draw();
</script>
<canvas width="600" height="600" id="my_canvas"></canvas> <script> function Rectangle(x, y, width, height, color) { this.width = width; this.height = height; this.draw = function () { var canvas = document.getElementById('my_canvas'); if (canvas.getContext) { var ctx = canvas.getContext('2d'); ctx.fillStyle = color; ctx.clearRect(x, y, width, height); ctx.fillRect(x, y, width, height); } } } let rectangle1 = new Rectangle(30, 50, 150, 50, 'green'); rectangle1.draw(); let rectangle2 = new Rectangle(300, 200, 300, 50, 'red'); rectangle2.draw(); </script>
<canvas width="600" height="600" id="my_canvas"></canvas>

    <script>

        function Rectangle(x, y, width, height, color) 
        { 
             this.width = width; 
             this.height = height; 
             
             this.draw = function () {
                var canvas = document.getElementById('my_canvas');
                if (canvas.getContext) {
                    var ctx = canvas.getContext('2d');
                    ctx.fillStyle = color;
                    ctx.clearRect(x, y, width, height);
                    ctx.fillRect(x, y, width, height);
                }
             } 
        }
        
        let rectangle1 = new Rectangle(30, 50, 150, 50, 'green');
        rectangle1.draw();
        
        let rectangle2 = new Rectangle(300, 200, 300, 50, 'red');
        rectangle2.draw();
    </script>

Here i have added the draw() method which draw rectangle using Javascript canvas api. Then i declared two instances of the Rectangle, and passing different parameters for x,y, width, height, and color. Finally calling draw on each object.

 

In addition to that we can also add properties and methods to already created objects to add new functionality to that object. For example let’s add a method to calculate the area of rectangle:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rectangle1.getArea = function() {
return this.width * this.height;
}
console.log(rectangle1.getArea());
// output
7500
rectangle1.getArea = function() { return this.width * this.height; } console.log(rectangle1.getArea()); // output 7500
rectangle1.getArea = function() {
    return this.width * this.height;
}
        
console.log(rectangle1.getArea());

// output
7500

However this method is only available on rectangle1 only not shared across any Rectangle object.

So any attempt to invoke getArea() on rectangle2 you will get an error:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(rectangle2.getArea()); // Uncaught TypeError: rectangle2.getArea is not a function
console.log(rectangle2.getArea()); // Uncaught TypeError: rectangle2.getArea is not a function
console.log(rectangle2.getArea());   // Uncaught TypeError: rectangle2.getArea is not a function

This is some of the advantages of constructors is that as i mentioned above every object has it’s own state. But if you need that the getArea() method to be available globally on every instance, there is a way using the object prototype. 

 

Object Prototypes

According to MDN prototypes are the mechanism by which JavaScript objects inherit features from one another. Every object in javascript has it’s own prototype which enable us to modify that object to add certain properties and methods.

You can get the prototype of any object using Object.getPrototypeOf() method like so:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(Object.getPrototypeOf(rectangle1));
console.log(Object.getPrototypeOf(rectangle1));
console.log(Object.getPrototypeOf(rectangle1));

If you run this you will see something similar to this output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{constructor: ƒ}
constructor: ƒ Rectangle(x, y, width, height, color)
....
....
{constructor: ƒ} constructor: ƒ Rectangle(x, y, width, height, color) .... ....
{constructor: ƒ}
   constructor: ƒ Rectangle(x, y, width, height, color)
   ....
   ....

The object prototype displays the structure of the object which consists of the function argument, caller, length, name, and other info like toString(), valueof.

Using the object prototype we can easily add methods and properties to an existing object which will be available globally across all instances of that object.

So let’s modify the getArea() method to be accessible to every instance of Rectangle:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Rectangle.prototype.getArea = function() {
return this.width * this.height;
}
console.log("Rectangle 1 area: ", rectangle1.getArea());
console.log("Rectangle 2 area: ", rectangle2.getArea());
Rectangle.prototype.getArea = function() { return this.width * this.height; } console.log("Rectangle 1 area: ", rectangle1.getArea()); console.log("Rectangle 2 area: ", rectangle2.getArea());
Rectangle.prototype.getArea = function() {
    return this.width * this.height;
}
        
console.log("Rectangle 1 area: ", rectangle1.getArea());
                
console.log("Rectangle 2 area: ", rectangle2.getArea());

output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Rectangle 1 area: 7500
Rectangle 2 area: 15000
Rectangle 1 area: 7500 Rectangle 2 area: 15000
Rectangle 1 area:  7500
Rectangle 2 area:  15000

Also we can add properties:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Rectangle.prototype.shapeType = "Rectangle";
console.log(rectangle1.shapeType);
// output
Rectangle
Rectangle.prototype.shapeType = "Rectangle"; console.log(rectangle1.shapeType); // output Rectangle
Rectangle.prototype.shapeType = "Rectangle";

console.log(rectangle1.shapeType);

// output
Rectangle

 

In the same way you can modify existing Javascript constructors like String, Number, etc. For example imagine you want to add a method to capitalize every word in string. We can add this method as follows in the String prototype:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
String.prototype.capitalize = function() {
return this.toLowerCase().replace(/\b[a-z]/g, function(a) {
return a.toUpperCase();
});
}
const str = "hello world";
console.log(str.capitalize()); // Hello World
String.prototype.capitalize = function() { return this.toLowerCase().replace(/\b[a-z]/g, function(a) { return a.toUpperCase(); }); } const str = "hello world"; console.log(str.capitalize()); // Hello World
String.prototype.capitalize = function() {
    return this.toLowerCase().replace(/\b[a-z]/g, function(a) {
            return a.toUpperCase();
      });
}
        
const str = "hello world";
console.log(str.capitalize());  // Hello World

 

Freezing Objects

You can prevent an object from being modified either to not allow adding properties or methods or not to modify the object prototype by using Object.freeze(obj) method. This method accept either a literal object or an object created with a constructor function.

Let’s modify our previous example and call Object.freeze() on rectangle1, then modify the shapeType property:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Object.freeze(rectangle1);
rectangle1.shapeType = "Square";
console.log(rectangle1.shapeType);
Object.freeze(rectangle1); rectangle1.shapeType = "Square"; console.log(rectangle1.shapeType);
Object.freeze(rectangle1);
        
rectangle1.shapeType = "Square";
        
console.log(rectangle1.shapeType);

If you run this example you will see that the shapeType is still Rectangle not Square, and this is the intended behavior of Object.freeze(). In strict mode this example will trigger an error like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Uncaught TypeError: Cannot add property shapeType, object is not extensible
Uncaught TypeError: Cannot add property shapeType, object is not extensible
Uncaught TypeError: Cannot add property shapeType, object is not extensible

Also you can’t add new methods to frozen objects:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Object.freeze(rectangle1);
rectangle1.printWidth = function() {
console.log("My width", this.width);
}
console.log(rectangle1.printWidth()); // Uncaught TypeError: rectangle1.printWidth is not a function
Object.freeze(rectangle1); rectangle1.printWidth = function() { console.log("My width", this.width); } console.log(rectangle1.printWidth()); // Uncaught TypeError: rectangle1.printWidth is not a function
Object.freeze(rectangle1);

rectangle1.printWidth = function() {
    console.log("My width", this.width);
}
        
console.log(rectangle1.printWidth());     // Uncaught TypeError: rectangle1.printWidth is not a function
    

To detect that an object is frozen or not we can use Object.isFrozen(obj) method:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(Object.isFrozen(rectangle1)); // true
console.log(Object.isFrozen(rectangle1)); // true
console.log(Object.isFrozen(rectangle1)); // true

Some benefits from freezing objects when you intend to build a third party package or library and you don’t want users to modify the internal implementation of the library.

 

Checking For Object Properties

To check the existence for specific property in an object there is many ways. The first we can use hasOwnProperty() method. But this method return true if this property is a direct in the object.

For example:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(rectangle1.hasOwnProperty('width')); // true
console.log(rectangle1.hasOwnProperty('width')); // true
console.log(rectangle1.hasOwnProperty('width'));  // true

Or more better syntax:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(Object.prototype.hasOwnProperty.call(rectangle1, 'width')); // true
console.log(Object.prototype.hasOwnProperty.call(rectangle1, 'width')); // true
console.log(Object.prototype.hasOwnProperty.call(rectangle1, 'width'));  // true

For inherited properties false is returned

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(rectangle1.hasOwnProperty('toString')); // false
console.log(rectangle1.hasOwnProperty('toString')); // false
console.log(rectangle1.hasOwnProperty('toString'));  // false

To check for direct and inherited properties the “in” operator can be used:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log("width" in rectangle1); // true
console.log("toString" in rectangle1); // true
console.log("width" in rectangle1); // true console.log("toString" in rectangle1); // true
console.log("width" in rectangle1);     // true
console.log("toString" in rectangle1);  // true

To get the list of all properties of an object we can either of these methods:

  • Iterate the object with for..in
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
for(var i in rectangle1) {
console.log(i + ' -> ' + rectangle1[i]);
}
for(var i in rectangle1) { console.log(i + ' -> ' + rectangle1[i]); }
for(var i in rectangle1) {
     console.log(i + ' -> ' + rectangle1[i]);
}
  • Using getOwnPropertyNames() method:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Object.getOwnPropertyNames(rectangle1).forEach(function (val) {
console.log(val + ' -> ' + rectangle1[val]);
});
Object.getOwnPropertyNames(rectangle1).forEach(function (val) { console.log(val + ' -> ' + rectangle1[val]); });
Object.getOwnPropertyNames(rectangle1).forEach(function (val) {
           console.log(val + ' -> ' + rectangle1[val]);
 });

Note that the later method using getOwnPropertyName() doesn’t return the properties defined after the object is created like shapeType while the first method return all the properties.

4 1 vote
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest


0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments