Prototype is a powerful concept in JavaScript because it allows for code reuse and inheritance. In this article, I want to give an overview of this concept. Before that, I will introduce prototype-based programming languages. Then, I will explain the prototype concept in JavaScript using an example. Then, I will present an example of the built-in JavaScript prototype, ‘Date.prototype’. I will show how you can extend this prototype with new methods. I will end this article with some recommendations for using prototypes.
Prototype-based Programming Language
JavaScript is a prototype-based programming language. Prototype-based programming languages are languages in which object creation and behavior inheritance can be done by cloning existing objects that act as prototypes. Here we mention some common characteristics of these languages:
No Class: Unlike class-based languages, prototype-based languages do not use classes. Remember that class-based languages are mainly based on defining classes and creating objects from them. A class is an abstract concept that defines its objects’ common behavior (properties and methods). But, a prototype is a concrete concept, from which objects can be created by copying and extending. Prototype-based languages make object creation and inheritance easier as they create objects from the existing ones.
Prototype: The concept of the prototype is implemented in JavaScript using a built-in property [[Prototype]] (or Object.prototype) that belongs to each object. This property is a pointer to another object which is the prototype. As the prototype is an object, it also has a property [[Prototype]] that points to its prototype! This chain can continue until we reach a null prototype.
Prototype Chain: This is the chain of objects linked by the [[Prototype]] property. When you want to access a property or method of an object, JavaScript looks for the property in its direct properties. If it is not there, it looks for the property in the [[Prototype]] properties and so on. The search will only stop if the property is found or the prototype is null.
Delegation: In prototype-based language, an object can delegate behavior to its prototype.
Shared Properties and Methods: The prototype enables the sharing of properties and methods between objects. This fosters reusability and optimal memory usage.
Object Mutability: Objects are modified dynamically. At runtime, new properties and methods can be added and existing ones can be overridden.
Example of Prototype
Let’s illustrate with an example how an object can inherit from other objects using the prototype property:
const person = {
setName(name) {
this.name = name;
},
getName() {
return this.name;
}
};
Here we define an object called ‘person’ with a property ‘name’ and two methods ‘setName()’ and ‘getName()’. The method ‘setName()’ is declared to set the ‘name’ property and ‘getName()’ is declared to return the value of ‘name’. We can use ‘Object.create()’ method and the ‘person’ object as a prototype to create a new ‘employee’ object as follows:
const employee = Object.create(person);
employee.setSalary = function(salary) {
this.salary = salary;
};
employee.getSalary = function() {
return this.salary;
};
The ‘employee’ object can access the ‘name’ property, ‘setName()’ and ‘getName()’ methods of ‘person’ through delegation via its ‘person’ prototype. Moreover, ‘employee’ has its own properties (‘salary’) and methods (‘setSalary()’ and ‘getSalary()’).
employee.setName("Jack Roberts");
employee.setSalary(4000);
console.log(employee.getName()); // Jack Roberts
console.log(employee.getSalary()); //4000
We can extend the prototype chain by creating a new object ‘developer’ and setting ‘employee’ as its prototype:
const developer = Object.create(employee);
developer.task = "coding with Java";
If we call the ‘getName()’ and ‘getSalary()’ methods for a ‘developer’ object, we get those of the ‘employee’ object because ‘developer’ doesn’t have its own:
console.log(developer.getName()); // Jack Roberts
console.log(developer.getSalary()); //4000
If you try to call the ‘toString()’ method on the ‘developer’ object, you will get ‘[object Object]’. We haven’t defined this method anywhere but it exists! This method is not defined in ‘person’, ‘employee’, or ‘developer’. It is inherited from the object [[Prototype]] (Object.prototype).
console.log(developer.toString()); //[object Object]
You can explore the properties of this object using ‘console.log’:
console.log(developer);
As illustrated in the figure above, you will notice that ‘developer’ has only the ‘task’ property and [[prototype]] which points to ‘employee’ as its prototype. The ‘employee’ prototype points to the ‘person’ prototype. Finally, the ‘person’ prototype is linked to the object prototype. The object prototype is the one that has general properties and methods shared by all JavaScript objects like the ‘toString()’ method.
Note that you can customize any method of the prototype, for example by overriding the ‘toString()’ method:
developer.toString =function() {
console.log("I am a tool that converts caffeine to code!");
};
In summary, the prototype chain looks like this:
developer -> employee -> person -> Object.prototype -> null
So, to look for an available property or method of the object ‘developer’, JavaScript searches for it in ‘developer’, if it is not found it looks for it in the ‘employee’, then ‘person’, and finally ‘Object.prototype’. The search will be stopped only if the property or method is found or a null prototype is reached.
Date.prototype
Prototypes in JavaScript help you to augment objects by adding new properties and methods. The objects you can extend are user-defined or built-in objects. We take here an example of a built-in object in JavaScript which is ‘Date’. We will add some methods to ‘Date.prototype’ to extend the behavior of all instances of ‘Date’ (shared properties). To explore the contents of ‘Date.prototype’, type it into the Chrome console and see what you get. There are many predefined methods that new instances of ‘Date’ can reuse:
Fig 2. Part of the properties of the ‘Date.prototype’
Extending Date.prototype
‘Date.prototype’ methods are basic and insufficient for real applications. In general, applications often need specific methods like displaying dates with a particular format or checking if a day is a workday or a weekend. If such methods are frequently called in your application, it is better to integrate them into ‘Date.prototype’. You can reuse these methods as they are now available for any ‘Date’ instance. The following table contains some examples illustrating the use of ‘Date.prototype’ in JavaScript.
JavaScript Date.prototype Examples |
||
Example of Method |
Code Snippet |
Code Output |
Custom formatting a date |
Date.prototype.appFormat = function() { let year = this.getFullYear(); let month = (this.getMonth() + 1).toString().padStart(2, '0'); let day = this.getDate().toString().padStart(2, '0'); return `${day}.${month}.${year}`; };
console.log((new Date()).appFormat()); console.log((new Date('2023-09-12')).appFormat()); |
27.07.2024 12.09.2023 |
Check if a date is a working day |
Date.prototype.isWorkingDay = function() { let day = this.getDay(); return (day > 0 && day < 6); };
console.log((new Date('2023-09-12')).isWorkingDay()); console.log((new Date('2024-10-13')).isWorkingDay()); |
true false |
Add days to a date |
Date.prototype.addDays = function(days) { const date = new Date(this.valueOf()); date.setDate(date.getDate() + days); return date; }; console.log(new Date().addDays(10)); console.log(new Date("2024-04-15").addDays(15)); |
Wed Aug 07 2024 11:04:31 GMT+0200 (Central European Summer Time) Tue Apr 30 2024 02:00:00 GMT+0200 (Central European Summer Time) |
No comments:
Post a Comment