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:
__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:
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__.
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:
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:
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
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:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("Hello, I am " + this.name);
};
const p = new Person("Abhishek");
Here:
Examples of constructor-capable functions
Functions that are NOT constructor-capable
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.