Prototype is fundamental concepts that every JavaScript developer should understand. In this comprehensive guide, we’ll dive deep into the concept of prototypes, uncover its significance, and explore how it can enhance your JavaScript development journey.
Contents
Understanding Prototypes
At its core, JavaScript is a prototype-based language. This means that objects can inherit properties and methods from other objects, known as prototypes. When a property or method is not found on an object itself, JavaScript looks up the prototype chain to find it. This concept of inheritance through prototypes forms the backbone of JavaScript’s object-oriented programming model.
The Prototype Property
In JavaScript, every object has a special property called prototype. This property points to another object, which is used as the source of properties and methods when the object is created through a constructor function or a class. Let’s take a look at an example:
const person = { name: 'John', age: 30 } // OR const person = new Object({ name: 'John', age: 30 }) // Create custom function in parent prototype object Object.prototype.customFunc = function() { console.log('Hi') } // create object instance from our object person const Martin = Object.create(person)
// Primitives // By default, a primitive does not have a parent object. but if we refer to a primitive in our program as objects through // ".", then an object version of this primitive is temporarily created in memory let age = 30 // new Number(...) let site = 'jottup' // new String(...) let isActive = true // new Boolean(...) let promise = new Promise(() => {}) // new Promise(...) let male = {} // new Object(...) let peoples = [] // new Array(...) function subscribeTutorials() {} // new Function(...) let likeTutorial = function() {} // new Function(...) let clickTheTutorial = () => {} // new Function(...) class bestTutorials {} // new Function(...) let tutorial = new bestTutorials() // new bestTutorials
All objects has property _proto_, but not all objects has prototype
let male = {} // This is an object and the object has __proto__ let peoples = [] // This is an array, basically it is an object on more low js level, object has _proto_ // Primitives let age = 30 // when accessed as an object, a shell is temporarily created Number(), has _proto_ let site = 'jottup' // when accessed as an object, a shell is temporarily created String(), has _proto_ let isActive = true // when accessed as an object, a shell is temporarily created Boolean(), has _proto_ function subscribeTutorials() {} // created in memory as Function, has _proto_ let likeTutorial = function() {} // created in memory as Function, has _proto_ let clickTheTutorial = () => {} // created in memory as Function, has _proto_ class bestTutorials {} // class it is a Function, syntactic sugar from ES, has _proto_
Moreover, different _proto_ objects of different “type” are completely different objects.
For objects of the same “type” – they are equal, that is, they are one and the same object
let person = {} let person1 = {} console.log(person === person1) // False console.log(person.__proto__ === person1.__proto__) // True
Each prototype is an independent object, in itself, with a certain set of properties and methods. Prototype is either in Class or function.
class Tutorial {} function Component() {} // is a constructor function, because there is a function const sum = function() {} // is a constructor function, because there is a function console.log(Tutorial.prototype) console.log(Component.prototype) console.log(sum.prototype) console.log(Object.prototype) console.log(Promise.prototype) console.log(Function.prototype) console.log(Boolean.prototype) console.log(Number.prototype) console.log(String.prototype) console.log(Array.prototype) // Extra Cases const increase = () => {} // the arrow function does not have a prototype because it is not a constructor function!!! let person = {name: 'John'} console.log(person.prototype) // prototype not exist, because it wasn't created via class or function console.log(person.__proto__ === Object.prototype) // True
Prototypal Inheritance
JavaScript’s prototype chain allows for efficient and flexible inheritance. When an object’s property or method is accessed, and it’s not found on the object itself, JavaScript searches up the prototype chain until it finds the property/method or reaches the end of the chain. This mechanism enables powerful inheritance, which is leveraged by many frameworks and libraries.
Prototype inheritance is inheritance from objects. Each entity in javascript has a _proto_ property, which is essentially a reference to the object’s parent prototype.
Both _proto_ and prototype are object properties.
_proto_ of any object refers to the prototype of the class (constructor function) with which this object was created.
let age = 30 // age.__proto__ === Number.prototype let site = 'jottup' // site.__proto__ === String.prototype let isActive = true // isActive.__proto__ === Boolean.prototype let promise = new Promise(() => {}) // promise.__proto__ === Promise.prototype let male = {} // male.__proto__ === Object.prototype let peoples = [] // peoples.__proto__ === Array.prototype function subscribeTutorials() {} // subscribeTutorials.__proto__ === Function.prototype let likeTutorial = function() {} // likeTutorial.__proto__ === Function.prototype let clickTheTutorial = () => {} // clickTheTutorial.__proto__ === Function.prototype class bestTutorials {} // bestTutorials.__proto__ === Function.prototype let tutorial = new bestTutorials() // tutorial.__proto__ === bestTutorials.prototype
Why does a class need a prototype object?
Why would objects created with this class need a __proto__ property that references this prototype object?
If we are trying to read the property of an object, or call its method, but this property or method does not exist, then the object will climb to look for it through the __proto__ reference in the prototype of the class with which it was created.
let person = {name: 'John'} person.toString() // person.__proto__ => Object.prototype = { toString(){} }
The Object.create() Method
ES5 introduced the Object.create() method, providing developers with an alternative way to create objects with specific prototypes. This method allows you to explicitly define the prototype of a newly created object:
const vehiclePrototype = { drive() { console.log("Vroom vroom!"); } }; const myCar = Object.create(vehiclePrototype); myCar.drive(); // Output: Vroom vroom!
ES6 and Classes
With the advent of ECMAScript 2015 (ES6), JavaScript introduced the class syntax, making object-oriented programming more intuitive for developers coming from class-based languages. Under the hood, classes still utilize prototypes for inheritance.
How works in background Ecmascript classes?
class Tutorial { constructor(name) { this.name = name } hello() { console.log(this.name) } } // the same as function Tutorial(name) { this.name = name } Tutorial.prototype.hello = function() { console.log(this.name) }
Manipulating Prototypes
While modifying built-in object prototypes is generally discouraged due to potential conflicts and compatibility issues, it’s essential to understand how prototypes can be manipulated. Remember, changes made to the prototype will affect all instances and derived objects.
Adding Methods to Built-in Objects
Array.prototype.sum = function() { return this.reduce((total, num) => total + num, 0); }; const numbers = [1, 2, 3, 4, 5]; console.log(numbers.sum()); // Output: 15
Changing Prototype Reference
function Animal(name) { this.name = name; } function Dog(name, breed) { this.breed = breed; } Dog.prototype = new Animal(); const myDog = new Dog("Buddy", "Golden Retriever"); console.log(myDog instanceof Animal); // Output: true