DEV Community

sanderdebr
sanderdebr

Posted on

What is prototype in JavaScript?

JavaScript currently has seven primitives which are: Boolean, Null, Undefined, Number, BigInt, String and Symbol. Everything else is an object— including arrays and dates.

const myArr = [];
typof myArr // returns "object"
const yourArr = new Array;
typof yourArr// also returns "object"

You’re probably used to using the .slice() or .push() methods often on arrays. Where do these build-in methods come from?

const fruits = ['apple', 'peach', 'kiwi'];
fruits.pop(); // removes kiwi

They come from the methods of the prototype of the array object you created.

const apples = new Array(3).fill('apple');
console.log(Array.prototype); // returns methods of Array prototype

When you log the above you will see all the methods that are build-in and ready to use. When you call pop() your are actually calling a function (method) of the prototype object your array is part of. The object that you are calling becomes the value of the this keyword in the method(). So when you do:

apples.pop(); // removes and apple

the apples array is set as the value of the this keyword. The .slice() method is just a shorthand we are using

Array.prototype.pop.call(apples);

With call we can borrow methods to use on objects that contain the this keyword (like Array). So actually, we can borrow the pop() method from the Array prototype to use on array-like objects:

const fishes = {0: 'Neo', 1: 'Clown', 2: 'Angel', length: 3};
Array.prototype.pop.call(fishes);

Object are foundational to almost every aspect of the JavaScript language. The most common way is to create them with curly braces and add properties and methods using dot notation.

const fish = {};
fish.name = 'Nemo';
fish.food = 5;
fish.eat = function(food, amount) { 
   console.log(`${this.name} is eating ${food}`);
   this.food += amount
}

What if we want to create more fishes? We can place the login inside a function and invoke it when we want to create a new fish.

Functional Instantiation

function Fish(name, food) {
   const fish = {};
   fish.name = name;
   fish.food = food;
   fish.eat = function(food, amount) { 
      console.log(`${this.name} is eating ${food}`);
      this.food += amount
   }
   return fish
}
const nemo = Fish('Nemo', 5);
const angel = Fish('Angel', 5);

The eat method is generic, so we are wasting memory by using the above function and making each fish bigger than it needs to be.

Functional Instantiation with shared methods

const fishMethods = {
   eat(food, amount) { 
   console.log(`${this.name} is eating ${food}`);
   this.food += amount
}

}
function Fish(name, food) {
   const fish = {};
   fish.name = name;
   fish.food = food;
   fish.eat = fishMethods.eat
   return fish
}
const nemo = Fish('Nemo', 5);
const angel = Fish('Angel', 5);

We’ve solved the problem of memory waste. It still seems a bit weird using a seperate object with methods in order to share methods across instances. Well, that is where prototypes come into place!

Instead of having a seperate object for methods, we call add the methods directly to the prototype of Fish.

Prototypal Instantiation

function Fish(name, food) {
   const fish = {};
   fish.name = name;
   fish.food = food;
   fish.eat = fishMethods.eat
   return fish
}
Fish.prototype.eat = function(food, amount) { 
  console.log(`${this.name} is eating ${food}`);
   this.food += amount
}
const nemo = Fish('Nemo', 5);
const angel = Fish('Angel', 5);

Boom! All the functionality is the same, but instead of having to manage a seperate object for all the methods we can just use a built in object from Fish called Fish.prototype.

Object.create

To improve our code even more, we will using Object.create() to avoid failed lookups. This method creates a new object with the specified prototype.

const fish = Object.create(Fish.prototype);

The ‘new’ keyword

When you invoke a function with the new keyword, it automatically assigns the this keyword to the new object that is created. So we do not need to assign the this and return it anymore.

function Fish(name, food) {
   // const this = Object.create(Fish.prototype);
   fish.name = name;
   fish.food = food;
   fish.eat = fishMethods.eat
   // return this
}

const nemo = new Fish('Nemo', 5);
const angel = new Fish('Angel', 5);

Classes

A class creates a blueprint for an object, basically the same thing as we just made. There is an easier way to do it and JavaScript isn’t a dead language. So, in 2015, EcmaScript 6 introduced support for Classes and the ‘class’ keyword. How are code looks like with a class:

class Fish {
      constructor(name, food) {
      this.name = name;
      this.food = food;
   }
   eat(food, amount) { 
      console.log(`${this.name} is eating ${food}`);
      this.food += amount
   }
}

const nemo = new Fish('Nemo', 5);
const angel = new Fish('Angel', 5);

Looks much better, right?

Why is it still helpful to learn how to do it the old way? Because classes are actually *syntactic sugar *over the old way. The class gets converted to the old way!

Static methods

What if we want a method that we want to use on the class (object constructor) but not in each instance of the class? We just add the static keyword before our method.

class Person {
   constructor(name, age) {
      this.name = name;
      this.age = age;
   }
   walk(time) { 
      console.log(`${this.name} is walking for ${time} minutes.`) 
   }
   static sayHi() { 
      alert('Hi!') 
   }
}
const tim = new Person('Tim', 25);
tim.walk(5) // works
tim.sayHi() // does not work
Person.sayHi() // works

That’s it, have fun creating objects!

Top comments (0)