Intro to Object Prototypes in JavaScript

Aug 7, 2019

When you create a new object in JavaScript using {}, it comes with some built-in properties, like a toString() function.

const obj = {};
obj.toString(); // '[object Object]'

The Mozilla docs document this function as Object.prototype.toString(). That's because obj is an instance of the Object class.

When you access the toString property, JavaScript first looks to see if obj has a toString property. Since it doesn't, JavaScript goes up the inheritance chain to Object.prototype, and checks if Object.prototype has a toString property.

const obj = {};
obj instanceof Object; // true
obj.toString === Object.prototype.toString; // true

obj.toString = () => {};
obj.toString === Object.prototype.toString; // false

You can think of Object.prototype as a template object that all objects inherit methods and properties from.

Adding Properties to a Prototype

A prototype is a JavaScript object like any other. That means you can add new properties to Object.prototype, and then every object will have access to that property.

// Add a `getAnswer()` function to _all_ objects
Object.prototype.getAnswer = function() { return 42 };

const obj = {};
obj.getAnswer(); // 42

Just because you can add methods to Object.prototype doesn't mean you should. Doing so may cause compatibility issues with future versions of JavaScript. For example, the famous SmooshGate debacle was caused because a popular library added a Array.prototype.flatten() that conflicted with a new built-in JavaScript function.

Creating Your Own Prototype

Suppose you have a pre-ES6 JavaScript class, which is just a plain old function that you will call with new.

function MyClass() {}

The MyClass function has a prototype property that you can modify.

function MyClass() {}

// Add a `getAnswer()` function to all instances of `MyClass`
MyClass.prototype.getAnswer = function() { return 42; };

const obj = new MyClass();
obj.getAnswer(); // 42

You can also overwrite the MyClass function's prototype entirely.

function MyClass() {}

// Overwrite the entire prototype
MyClass.prototype = {
  getAnswer: function() { return 42; }
};

const obj = new MyClass();
obj.getAnswer(); // 42

Inheriting from Another Class

The prototype object doesn't need to be a plain object. It can be an instance of another class. To create a class MyChildClass that inherits from MyClass, you set the MyChildClass prototype to be an instance of MyClass.

function MyClass() {}

// Overwrite the entire prototype
MyClass.prototype = {
  getAnswer: function() { return 42; }
};

function MyChildClass() {}
MyChildClass.prototype = new MyClass();

const obj = new MyChildClass();
obj.getAnswer(); // 42

// `obj` is an instance of `MyChildClass`, and `MyChildClass` inherits
// from `MyClass`, which in turn inherits from `Object`.
obj instanceof MyChildClass; // true
obj instanceof MyClass; // true
obj instanceof Object; // true

MyChildClass inherits from MyChild, which in turn inherits from Object. That's because MyChildClass.prototype is an instance of MyClass, and then MyClass.prototype is an instance of object. This is what JavaScript developers call the prototype chain.

Get An Object's Prototype

Given an object, you can get access to its prototype using .constructor.prototype.

function MyClass() {}

const obj = new MyClass();
obj.constructor.prototype.getAnswer = function() { return 42; };

const obj2 = new MyClass();
obj2.getAnswer(); // 42

That's because there's an Object.prototype.constructor property that points to the object's constructor. There's also a non-standard __proto__ property that behaves similarly to constructor.prototype.

The constructor and __proto__ properties are potential attack vectors for prototype poisoning. Several popular JavaScript libraries, including lodash and Mongoose, have reported prototype poisoning vulnerabilities in the past.


Did you find this tutorial useful? Say thanks by starring our repo on GitHub!

More Fundamentals Tutorials