Skip to main content

Command Palette

Search for a command to run...

Javascript Prototypes

Published
5 min read

JavaScript prototypes are a fundamental part of the language's object-oriented programming model. They are the mechanism by which JavaScript objects inherit features from one another

Every Object in JS has a prototype property, we use prototype chaining to get to a value, JS will keep looking deeper into the object untill it finds null as a prototype response, this is how we are able to inherit one property of one object with the other object.

Inheritance: When you try to access a property or method on an object, JavaScript first looks for it on the object itself. If it's not found, JavaScript looks for it on the object's prototype. If it's still not found, it looks on the prototype's prototype, and so on, up the prototype chain.

  • Constructor Functions and Prototypes: When you create an object using a constructor function (or a class, which is syntactic sugar over prototypes), the constructor function's prototype property becomes the [[Prototype]] of the objects created with that constructor

class Life {
  constructor() {
    // Example: this.alive = true;
    // COnstructor is a method that is automatically called when you initialized the new instance of the class its purpose is to initialized properties
    // JS defines an empyt default constructor if it's not already defined
  }
}

// THe extends sets up the relationship between the parent and child class
// the super keyword is called in the constructor of the child class so it gives inherited members accesss.

// extends calls the parent's constructor and makes the inherited methods accessable.

class Animal extends Life {
  constructor(props) {
    super(props);
    this.props = props;
  }
}

// Sample question
// You need to implement the Counter constructor function and its prototype methods

function Counter() {
  // Initialize count property
  this.count = 0;
}

// Define increment method on Counter's prototype
Counter.prototype.increment = function () {
  this.count = this.count + 1;
};

// Define decrement method on Counter's prototype

Counter.prototype.decrement = function () {
  this.count = this.count - 1;
};

count.increment();
count.increment();
count.increment();
count.increment();
console.log(count); // 4 
count.decrement();
console.log(count); // 3

Inheritance in prototype

Inheritance refers to passing down the characteristics from the parent to the chile element, here the prototype can be used to do that.

Inheriting properties

JavaScript objects are dynamic "bags" of properties (referred to as own properties).

JavaScript objects have a link to a prototype object. When trying to access a property of an object, the property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached.


There are many ways to specify the prototype of an object for now lets continue with __proto__

Prototype is just an internal property of an object.

const o = {
  a: 1,
  b: 2,
  // __proto__ sets the [[prototype]] as another object literal
  __proto__: {
    b: 3,
    c: 4,
  },
};

// o.[[Prototype]] has properties b and c.
// o.[[Prototype]].[[Prototype]] is Object.prototype (we will see it later)
// Finally, o.[[Prototype]].[[Prototype]].[[Prototype]] is null.

console.log(o.a); //1
console.log(o.b); //2
// we didn't got 3 as we were looking for b in the own property, we got it so we didn't look for its prototype property

// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited.
// This is called Property Shadowing


console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is Object.prototype and
// there is no 'd' property by default, check its prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined.

Inheriting “methods“

we can inherit methods using prototype and as we do so, we will get all the properties of a parent component into the child component,

IF someone asks you to make inheritance without the word extends we can do so with this

const parent = {
  value: 2,
  method() {
    return this.value + 1;
  },
};

console.log(parent.method()); // 3
// When calling parent.method in this case, 'this' refers to parent

// child is an object that inherits from parent
const child = {
  __proto__: parent,
};
console.log(child.method()); // 3
// When child.method is called, 'this' refers to child.
// So when child inherits the method of parent,
// The property 'value' is sought on child. However, since child
// doesn't have an own property called 'value', the property is
// found on the [[Prototype]], which is parent.value.

child.value = 4; // assign the value 4 to the property 'value' on child.
// This shadows the 'value' property on parent.
// The child object now looks like:
// { value: 4, __proto__: { value: 2, method: [Function] } }
console.log(child.method()); // 5
// Since child now has the 'value' property, 'this.value' means
// child.value instead

Constructors

the functional way of writing the prototype method to the boxes is .box method

// A very bad approach
//const boxes = [
//  { value: 1, getValue() { return this.value; } },
//  { value: 2, getValue() { return this.value; } },
//  { value: 3, getValue() { return this.value; } },
//];


// A constructor function
function Box(value) {
  this.value = value;
}

// Properties all boxes created from the Box() constructor
// will have
Box.prototype.getValue = function () {
  return this.value;
};

const boxes = [new Box(1), new Box(2), new Box(3)];

Class based way of doing the same

class Box {
  constructor(value) {
    this.value = value;
  }

  // Methods are created on Box.prototype
  getValue() {
    return this.value;
  }
}

function Box(value) {
  this.value = value;
}
Box.prototype.getValue = function () {
  return this.value;
};
const box = new Box(1);

// Mutate Box.prototype after an instance has already been created
Box.prototype.getValue = function () {
  return this.value + 1;
};
box.getValue(); // 2

Implicit constructors of literals

Some literal syntaxes in JavaScript create instances that implicitly set the [[Prototype]]. For example:

jsCopy to Clipboard

// Object literals (without the `__proto__` key) automatically
// have `Object.prototype` as their `[[Prototype]]`
const object = { a: 1 };
Object.getPrototypeOf(object) === Object.prototype; // true

// Array literals automatically have `Array.prototype` as their `[[Prototype]]`
const array = [1, 2, 3];
Object.getPrototypeOf(array) === Array.prototype; // true

// RegExp literals automatically have `RegExp.prototype` as their `[[Prototype]]`
const regexp = /abc/;
Object.getPrototypeOf(regexp) === RegExp.prototype; // true

// Refer to this doc for getting the best explanation regarding prototype and __proto__.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain