Understanding JavaScript Prototype: A Complete Guide

Understanding JavaScript Prototype: A Complete Guide

JavaScript’s prototype system is one of the most powerful yet confusing concepts for developers. Much of the confusion comes from terms like prototype, __proto__, constructor functions, and inheritance being used interchangeably. To truly understand JavaScript, it is important to understand how prototypes work behind the scenes.


JavaScript Is Prototype-Based

JavaScript is not class-based like Java or C++. Instead, it is a prototype-based language, meaning objects inherit directly from other objects. Even though ES6 introduced the class keyword, JavaScript still uses prototypes internally. Classes are simply syntax sugar over the prototype system.


What Is a Prototype?

Every JavaScript object has an internal hidden property called [[Prototype]]. This internal link points to another object and is used when JavaScript looks up properties or methods. If a property is not found on the object itself, JavaScript checks its prototype, then the prototype’s prototype, and so on, until it reaches null.

This lookup path is known as the prototype chain.


prototype vs __proto__

Although they sound similar, prototype and __proto__ serve different purposes.

prototype

prototype is a property that exists on functions that can be used as constructors (constructor-capable functions). It is used to define properties and methods that should be shared by all objects created using that constructor.

Key points:

  • Only constructor functions have .prototype
  • Normal objects do not have .prototype
  • Arrow functions do not have .prototype because they cannot be used as constructors


__proto__

__proto__ is an accessor that exposes an object’s internal [[Prototype]]. It exists on almost all JavaScript objects, including arrays, functions, and instances created using constructors.

Important clarifications:

  • __proto__ is not the real internal property
  • The real internal property is [[Prototype]]
  • __proto__ simply provides access to it


Relationship Between prototype and __proto__

The most important rule to remember is:

Constructor.prototype === instance.__proto__

This means that when an object is created using a constructor function, the object’s internal [[Prototype]] points to the constructor’s prototype object. This is how inheritance is established in JavaScript.


Do Constructor Functions Have Both?

Yes. Constructor functions have both .prototype and __proto__.

  • They have .prototype because they define inheritance for the objects they create
  • They have __proto__ because functions themselves are objects

As a result, constructor functions participate in two prototype chains: one as constructors and one as objects.


Prototype Chain

When a property is accessed on an object, JavaScript follows these steps:

  1. Look for the property on the object itself
  2. If not found, look at the object’s prototype
  3. Continue up the chain
  4. Stop when null is reached

This sequence is called the prototype chain, and it controls how JavaScript resolves property access.


Is __proto__ Present in All Objects?

Almost all objects expose __proto__, but there is an important exception.

Objects created using Object.create(null) still have an internal [[Prototype]], but it is set to null, and they do not inherit the __proto__ accessor from Object.prototype.

So the accurate statement is:

Every object has [[Prototype]], but not every object exposes it via __proto__.


Prototype Inheritance

Prototype inheritance is the mechanism by which objects inherit properties and methods from other objects through the prototype chain. Methods are shared, not copied, making this approach memory-efficient.

Objects can inherit from other objects directly or through constructor functions. In both cases, inheritance is handled by the prototype chain.


Property Read vs Write Rule

A critical rule to remember:

  • Reading a property walks the prototype chain
  • Writing a property creates or updates the property directly on the object itself

Writing does not affect the prototype.


ES6 Classes and Prototypes

ES6 classes do not change JavaScript’s inheritance model. Under the hood, class methods are added to the constructor’s prototype, and instances still rely on the prototype chain.

Classes simply provide a cleaner and more familiar syntax.


Summary

  • JavaScript uses prototype-based inheritance
  • Every object has an internal [[Prototype]]
  • __proto__ exposes the internal prototype
  • .prototype exists only on constructor functions
  • Constructor.prototype === instance.__proto__
  • Constructor functions have both .prototype and __proto__
  • The prototype chain controls property lookup
  • ES6 classes are syntax sugar over prototypes


Constructor-Capable Functions

Constructor-capable functions are functions that can be used with the new keyword to create objects.

When a function is constructor-capable, JavaScript automatically gives it a .prototype property. This prototype object is used to define properties and methods that should be shared by all objects created using that function.

What happens when you use new?

When you call a constructor-capable function with new:

  1. A new empty object is created
  2. The new object’s __proto__ is linked to the constructor’s .prototype
  3. The constructor function is executed with this bound to the new object
  4. The object is returned (unless the constructor explicitly returns another object)

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function () {
  console.log("Hello, I am " + this.name);
};

const p = new Person("Abhishek");
        

Here:

  • p.__proto__ === Person.prototype ✅
  • sayHello is not copied, it is accessed via the prototype chain

Examples of constructor-capable functions

  • Regular functions declared with function
  • ES6 classes (class User {})
  • Built-in constructors like Array, Object, Date

Functions that are NOT constructor-capable

  • Arrow functions
  • Method shorthand functions

These functions do not have a .prototype property because they cannot be used with new.


Common Misconceptions

Misconception 1: prototype exists only on functions explicitly used as constructors Reality: Any function that is constructor-capable has a .prototype, even if you never use it with new.

Misconception 2: All functions have .prototype Reality: Arrow functions and method shorthand functions do not have .prototype because they cannot be used with new.

Misconception 3: __proto__ and prototype are the same Reality: prototype belongs to constructor functions, while __proto__ belongs to objects and points to their internal [[Prototype]].

Misconception 4: Methods are copied during inheritance Reality: Methods are not copied. Objects reference them through the prototype chain.

Misconception 5: ES6 classes change inheritance Reality: Classes are syntax sugar. JavaScript still uses prototype-based inheritance under the hood.


Final Takeaway

In JavaScript, inheritance works through prototypes, where constructor-capable functions define shared behavior using .prototype, and objects access that behavior through __proto__ via the prototype chain.

To view or add a comment, sign in

More articles by Abhishek Kumar

Explore content categories