Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Prototypes

Classes in JavaScript are a convenient syntax on top of an older mechanism — prototypes. It is useful to know: it explains where an array gets its map() method and an object its toString(), even though you never declared them.

An object's prototype

Every object has a hidden link to another object — its prototype. If a property is not found in the object itself, JavaScript looks for it in the prototype. You can create an object with a chosen prototype using Object.create():

const animal = { eats: true };
 
const rabbit = Object.create(animal);
 
console.log(rabbit.eats); // true

rabbit has no eats property of its own — it was found in the prototype animal.

Own and inherited properties

Properties written directly into an object are called its own. Object.hasOwn() tells them apart from those found in the prototype:

const animal = { eats: true };
const rabbit = Object.create(animal);
rabbit.jumps = true;
 
console.log(Object.hasOwn(rabbit, 'jumps')); // true
console.log(Object.hasOwn(rabbit, 'eats'));  // false

If you write a property with the same name as in the prototype, the own value hides the inherited one, and the prototype itself does not change.

The prototype chain

A prototype can have its own prototype too — this forms a chain. The property lookup goes up the chain until the property is found or the chain ends. At the end of almost every chain is Object.prototype — that is where all objects get toString() from:

const user = { name: 'Anna' };
 
console.log(Object.getPrototypeOf(user) === Object.prototype); // true
console.log(typeof user.toString); // function

Object.getPrototypeOf() returns an object's prototype.

Class methods live in the prototype

When you declare a method in a class, it does not go into every object but into one shared object, ClassName.prototype. All instances get it through the prototype:

class User {
  greet() {
    return 'Hello!';
  }
}
 
const anna = new User();
const oleg = new User();
 
console.log(anna.greet === oleg.greet);                      // true
console.log(Object.getPrototypeOf(anna) === User.prototype); // true

The method is stored in one place, no matter how many objects are created. extends simply links the prototypes of two classes into a chain.

Built-in prototypes

The methods of arrays, strings and numbers live in Array.prototype, String.prototype and Number.prototype. That is why every array has map(), even though you never declared it:

const numbers = [1, 2, 3];
 
console.log(Object.getPrototypeOf(numbers) === Array.prototype); // true
console.log(Object.hasOwn(numbers, 'map'));                      // false

Common mistake

Built-in prototypes can be changed, but you should not do it. A method added to Array.prototype appears on every array in the program and may clash with a library method or a future version of the language with the same name:

// do not do this
Array.prototype.last = function () {
  return this[this.length - 1];
};

Write an ordinary function instead: const last = (array) => array.at(-1);.

Thanks for reading!

Donate to the site — we work hard for you.