![Javascript Constructor Functions and The this Keyword](https://webmobtuts.com/wp-content/uploads/2021/11/Javascript-Constructor-Functions-and-The-this-Keyword-800x400.jpg)
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:
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:
var rectangle = new Rectangle(); console.log(rectangle);
Now check the console you will see this output:
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:
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:
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:
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:
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:
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);
// 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:
function RectStyle(background, borderStyle) { this.background = background; this.borderStyle = borderStyle; }
Then create a new object from this function:
var rectangleStyle = new RectStyle("red", "dashed");
Then pass this argument as a third argument to Rectangle:
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:
let rectangle1 = new Rectangle(70, 20, rectangleStyle);
As this point you can access any rectangle style property from rectangle1:
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:
const str = new String("hello world"); console.log(str); // Object String {'hello'}
Also you can declare numbers:
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:
<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:
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:
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:
console.log(Object.getPrototypeOf(rectangle1));
If you run this you will see something similar to this output:
{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:
Rectangle.prototype.getArea = function() { return this.width * this.height; } console.log("Rectangle 1 area: ", rectangle1.getArea()); console.log("Rectangle 2 area: ", rectangle2.getArea());
output:
Rectangle 1 area: 7500 Rectangle 2 area: 15000
Also we can add properties:
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:
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:
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:
Uncaught TypeError: Cannot add property shapeType, object is not extensible
Also you can’t add new methods to frozen objects:
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:
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:
console.log(rectangle1.hasOwnProperty('width')); // true
Or more better syntax:
console.log(Object.prototype.hasOwnProperty.call(rectangle1, 'width')); // true
For inherited properties false is returned
console.log(rectangle1.hasOwnProperty('toString')); // false
To check for direct and inherited properties the “in” operator can be used:
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
for(var i in rectangle1) { console.log(i + ' -> ' + rectangle1[i]); }
- Using getOwnPropertyNames() method:
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.