Prototype
Objects in JavaScript have an internal property known as the prototype. It is simply a reference to another object and contains common attributes/properties shared across all instances of the object.
- An object's prototype attribute specifies the object from which it inherits properties.
- The prototype property is non-enumerable, meaning that it doesn't show up when we try to access the object's properties.
Prototype Chain
When an object gets a request for a property that it does not have, its prototype will be searched for the property, then the prototype's prototype, and so on until an object is reached with null as its prototype. By definition, null has no prototype and acts as the final link in this prototype chain.
Many objects don't directly have Object.prototype as their prototype, but instead have another object that provides a different set of default properties. Functions derive from Function.prototype, and arrays derive from Array.prototype.
In a prototypal system, objects inherit from objects.
Example
const user = { firstName: 'Vishwajeet' } console.log(user.firstName) // Vishwajeet console.log(user.lastName) // undefined console.log(user.toString()) // [object Object]
The toString() result is not undefined but an actual return value. This is because the toString() property lives in Object.prototype, which is connected to this object through the prototype chain.
Setting Prototype
The Object.setPrototypeOf() method sets the prototype of a specified object to another object or null.
const entity = { isHuman: true }; const vishwajeet = { firstName: 'Vishwajeet', lastName: 'Raj' }; Object.setPrototypeOf(vishwajeet, entity); console.log(vishwajeet.firstName) // Vishwajeet console.log(vishwajeet.isHuman) // true
The prototype chain is only searched when the property does not exist on the object.
for-in loop on Objects with Prototype
If you use a for..in loop to iterate over an object, any property that can be reached via its chain and is also enumerable will be enumerated. Use hasOwnProperty to count only the object's own properties:
const person = { name: 'Vishwajeet', lastName: 'Raj' } const extraDetails = { age: 23, eatsApples: true } Object.setPrototypeOf(person, extraDetails) let n = 0; for (let property in person) { if(person.hasOwnProperty(property)) { n++; } } console.log(n) // 2
Prototype delegation with the new keyword
The new keyword does the following things:
- Creates a blank, plain JavaScript object.
- Adds a property to the new object (
__proto__) that links to the constructor function's prototype object. - Binds the newly created object instance as the
thiscontext. - Returns
thisif the function doesn't return an object.
function Laptop(maker) { this.maker = maker this.ram = 4 } Laptop.prototype.ram = 8 Laptop.prototype.color = 'black' const myLaptop = new Laptop('Apple') console.log(myLaptop.ram) // 4 (found on the instance) console.log(myLaptop.color) // black (found on prototype) console.log(myLaptop.__proto__ === Laptop.prototype) // true
What's the difference between __proto__ and prototype?
The difference is that prototype is a property of a class constructor, while __proto__ is a property of a class instance.
Understanding Constructor Property
Every function has the prototype property even if we don't supply it. The default
prototypeis an object with a single property,constructor, that points back to the function itself.
function func() { console.log('some') } console.log(func.prototype.constructor) // [func function itself] const instance = new func(); console.log(instance.constructor === func) // true
The constructor property will not always point to the function that created it. We can assign the prototype to a new object:
function func() { console.log('some') } func.prototype = {} const instance = new func(); console.log(instance.constructor === func) // false console.log(instance.constructor === Object) // true
Prototype Delegation with class Keyword
The class keyword was introduced with ES6. It's just syntactic sugar over a regular JavaScript function.
class Human {} console.log(typeof Human) // function
Classes in JS use prototypal inheritance.
class Human { isAlive() { return true } } class Doctor extends Human { status() { return this.isAlive(); } } console.log(Human.prototype.isAlive()) // true const doc = new Doctor(); console.log(Object.getPrototypeOf(doc) === Doctor.prototype) // true console.log(Object.getPrototypeOf(Doctor.prototype) === Human.prototype) // true
The
extendskeyword creates what looks and acts similar to a classical parent-to-child relationship.
With the extends keyword, Human's prototype object is linked to Doctor's prototype object. We're able to access the isAlive function because it actually lives on Human's prototype object, not on the class itself.