1. Introduction
Prototypal inheritance is the mechanism that underlies all of JavaScript's object-oriented behavior, including classes. Every object has an internal link to another object called its prototype, and when a property or method is not found directly on an object, the JavaScript engine automatically looks it up the prototype chain until it finds it (or reaches null). Classes, extends, and even plain object literals all ultimately rely on this system.
Cricket analogy: Every player inherits fielding basics from the general "cricketer" prototype, and if a specific skill like bowling legspin isn't found on the individual player, the lookup walks up to the team's specialist coaching prototype until it finds it or reaches the end of the chain.
2. Syntax
function Vehicle(type) {
this.type = type;
}
// add a shared method to every Vehicle instance's prototype
Vehicle.prototype.describe = function () {
return `This is a ${this.type}`;
};
const car = new Vehicle("car");
Object.getPrototypeOf(car); // reads an object's prototype
Object.setPrototypeOf(car, proto); // sets an object's prototype3. Explanation
There are two related but distinct concepts that beginners often confuse: the prototype **property**, which exists only on functions/classes and points to the object that will become the prototype of instances created with new, and the internal [[Prototype]] link that every object has, accessible via Object.getPrototypeOf(obj) or the legacy accessor obj.__proto__. So car.__proto__ === Vehicle.prototype is true, but car.prototype is undefined — plain instances do not have their own prototype property.
Cricket analogy: The "Bowler" blueprint itself has a prototype property listing shared techniques, but an actual bowler created from it, like Bumrah, only has an internal link back to that blueprint (Bumrah.__proto__ === Bowler.prototype is true) — Bumrah himself has no prototype property of his own.
When class Bike extends Vehicle {} is declared, the engine sets Object.getPrototypeOf(Bike.prototype) === Vehicle.prototype, chaining the prototypes together so instances of Bike can find describe() even though Bike never defines it. Every plain object literal ({}) automatically has Object.prototype as its prototype, which is where methods like toString() and hasOwnProperty() ultimately come from.
Cricket analogy: When the "SpinBowler" role extends the general "Bowler" role, a spin bowler instance can still use the base appeal() method it never redefined, because the chain links back to Bowler.prototype — just like every plain scoresheet object automatically inherits toString() from Object.prototype.
obj.hasOwnProperty("describe") checks only the object's OWN properties, not inherited ones. So car.hasOwnProperty("describe") is false (it's inherited), while Vehicle.prototype.hasOwnProperty("describe") is true (it's defined directly there). This distinction is key to understanding where a property actually 'lives' in the chain.
4. Example
function Vehicle(type) {
this.type = type;
}
Vehicle.prototype.describe = function () {
return `This is a ${this.type}`;
};
const car = new Vehicle("car");
console.log(car.describe());
console.log(Object.getPrototypeOf(car) === Vehicle.prototype);
console.log(car.__proto__ === Vehicle.prototype);
console.log(car.hasOwnProperty("describe"));
console.log(Vehicle.prototype.hasOwnProperty("describe"));
class Bike extends Vehicle {}
const bike = new Bike("bike");
console.log(bike.describe());
console.log(Object.getPrototypeOf(Bike.prototype) === Vehicle.prototype);
const plainObj = { greet() { return "hi"; } };
console.log(Object.getPrototypeOf(plainObj) === Object.prototype);5. Output
This is a car
true
true
false
true
This is a bike
true
true6. Key Takeaways
- Every object has an internal prototype link; property lookups walk this chain until found or
nullis reached. Function.prototypeis a property that exists only on functions/classes, distinct from an instance's[[Prototype]].Object.getPrototypeOf(obj)is the standard way to read an object's prototype;__proto__is a legacy accessor for the same thing.hasOwnProperty()checks only an object's own properties, excluding inherited ones from the chain.class ... extendsis implemented by linkingDerived.prototype's prototype toBase.prototype.
Practice what you learned
1. What is the key difference between a function's `.prototype` property and an object's `[[Prototype]]` (`Object.getPrototypeOf`)?
2. Given `Vehicle.prototype.describe = function(){...}` and `const car = new Vehicle("car")`, what does `car.hasOwnProperty("describe")` return?
3. What is the recommended modern way to read an object's prototype, instead of `obj.__proto__`?
4. When `class Bike extends Vehicle {}` is declared, what is true about the prototype chain?
5. What is the prototype of a plain object literal like `{}`?
6. What happens during property lookup when a property is not found on an object itself?
Was this page helpful?
You May Also Like
Classes in JavaScript
How ES6 class syntax provides a cleaner, structured way to create objects and implement object-oriented patterns on top of JavaScript's prototype system.
Inheritance in JavaScript
How the `extends` and `super` keywords let one class build on another, sharing and overriding behavior while respecting strict initialization order.
Constructors in JavaScript
How constructor functions and class constructors initialize new objects, run automatically with `new`, and support default parameters.
Closures in JavaScript
Understand how closures let inner functions remember and access variables from their outer scope, and how loop variable capture can trip you up.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics