Prototypes & Inheritance Mechanics
Unlike traditional class-based object-oriented languages like Java or C++, JavaScript uses Prototypal Inheritance. Every JavaScript object possesses an internal link to another object called its Prototype, forming a chain used for property and method resolution.
The Prototype Chain
When you attempt to access a property or method on an object, the JavaScript engine follows a strict lookup procedure:
Prototype Resolution Chain
┌──────────────────────────────────────────────┐
│ myObject │
│ └─ [[Prototype]] -> DeveloperPrototype │
│ └─ [[Prototype]] -> Object.prototype │
│ └─ [[Prototype]] -> null │
└──────────────────────────────────────────────┘
- Checks if the property exists directly on
myObject(an own property). - If missing, traverses up the internal
[[Prototype]]link. - Continues climbing until it finds the property or reaches
Object.prototype.[[Prototype]], which isnull. - Returns
undefinedif the property is unfound anywhere along the chain.
prototype Property vs. __proto__
A common source of confusion in JavaScript is the distinction between Function.prototype and Object.__proto__.
| Entity | Description | Where It Exists |
|---|---|---|
prototype | Blueprint object assigned to instances created via new. | Exists only on Functions / Classes |
__proto__ | Historic getter/setter exposing an object's internal [[Prototype]]. | Exists on all Objects |
Object.getPrototypeOf() | Modern standard method to access an object's prototype. | Built-in static method |
function User(name) {
this.name = name;
}
User.prototype.sayHello = function () {
return `Hello, I'm ${this.name}`;
};
const alex = new User("Alex");
console.log(alex.__proto__ === User.prototype); // true
console.log(Object.getPrototypeOf(alex) === User.prototype); // true
Prototypal vs. Class-Based Syntax
ES6 introduced the class keyword. However, JavaScript classes are primarily syntactic sugar over the existing prototypal system—under the hood, functions and prototypes still power everything.
Prototypal Delegation Pattern
const animalActions = {
eat() {
return `${this.name} is eating.`;
}
};
// Create object linked directly to animalActions
const dog = Object.create(animalActions);
dog.name = "Rex";
console.log(dog.eat()); // "Rex is eating."
Modern ES6 Class Syntax
class Animal {
constructor(name) {
this.name = name;
}
eat() {
return `${this.name} is eating.`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call parent constructor
this.breed = breed;
}
bark() {
return `${this.name} barks loudly!`;
}
}
const rex = new Dog("Rex", "German Shepherd");
console.log(rex.eat()); // "Rex is eating." (Inherited from Animal)
console.log(rex.bark()); // "Rex barks loudly!"
Interactive Playground: Prototypal Chain Lookup
Inspect prototype linkage, property shadows, and method overrides in real time:
Best Practices
- Use ES6 Class Syntax for Readability: Prefer
classandextendsfor clean OOP structures, but remember it uses prototypes underneath. - Avoid Modifying Native Prototypes: Do not extend built-in objects like
Array.prototypeorObject.prototype(monkey patching), as it causes collisions with third-party libraries. - **Use
Object.getPrototypeOf()**: Avoid using the legacy__proto__accessor in production code; use standard methods likeObject.getPrototypeOf()andObject.setPrototypeOf().
Knowledge Check
Exercise Requirements:
- Implement a constructor function or class
Shapethat acceptscolor. - Extend
Shapewith aRectanglesubclass that acceptscolor,width, andheight, and includes a methodgetArea().
class Shape {
constructor(color) {
this.color = color;
}
}
class Rectangle extends Shape {
constructor(color, width, height) {
super(color);
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
const rect = new Rectangle("blue", 10, 5);
console.log(`Color: ${rect.color}, Area: ${rect.getArea()}`); // Color: blue, Area: 50
Now that you have mastered prototype delegation and class mechanics, proceed to Execution Context and Call Stack!