JavaScript's Inheritance Design
JavaScript's inheritance model operates through prototype chains rather than classical class-based inheritance. When Netscape needed a scripting language for web interactivity in 1994, Brendan Eich designed a system where objects inherit directly from other objects.
In classical languages like Java, inheritance works through classes:
class Animal {
void eat() { System.out.println("Eating"); }
}
class Dog extends Animal {
void bark() { System.out.println("Barking"); }
}
JavaScript achieves similar functionality through constructor functions and prototypes:
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(this.name + " is eating");
};
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log(this.name + " is barking");
};
Prototype Mechanism
Every JavaScript fucntion has a prototype property, and every object has a [[Prototype]] internal slot (accessible via __proto__ in many environments).
When you create an object using a constructor functon:
function Person(name) {
this.name = name;
}
const person = new Person("Alice");
The relationship is established where person.__proto__ === Person.prototype.
Property lookup follows the prototype chain:
Person.prototype.species = "Homo sapiens";
console.log(person.name); // "Alice" - found on instance
console.log(person.species); // "Homo sapiens" - found on prototype
console.log(person.age); // undefined - not found in chain
The lookup proceeds: instance → Person.prototype → Object.prototype → null.
Constructor Property
Each prototype object has a constructor property pointing back to its constructor funcsion:
console.log(Person.prototype.constructor === Person); // true
console.log(person.constructor === Person); // true
Modern Class Syntax
ES6 introduced class syntax as syntactic sugar over prototype-based inheritance:
class Vehicle {
constructor(type) {
this.type = type;
}
move() {
console.log(this.type + " is moving");
}
}
class Car extends Vehicle {
constructor(model) {
super("car");
this.model = model;
}
honk() {
console.log(this.model + " is honking");
}
}
This translates to essentially the same prototype chain structure.
Performance Considerations
Prototype chain traversal can impact performance when searching for properties deep in the chain. Use hasOwnProperty to check for direct properties:
const obj = { prop: "value" };
console.log(obj.hasOwnProperty("prop")); // true
console.log(obj.hasOwnProperty("toString")); // false - inherited
Modern JavaScript provides Object.hasOwn as a safer alternative:
console.log(Object.hasOwn(obj, "prop")); // true
Prototype Pollution Security
Modifying prototypes can lead to security vulnerabilities when attacker-controlled data affects object inheritance:
const user1 = { isAdmin: false };
// Malicious code modifies Object.prototype
Object.prototype.isAdmin = true;
const user2 = {};
console.log(user2.isAdmin); // true - polluted through prototype
Common attack vectors include unsafe merge operations:
function unsafeMerge(target, source) {
for (const key in source) {
target[key] = source[key];
}
}
const config = {};
const maliciousInput = JSON.parse('{"__proto__": {"isAdmin": true}}');
unsafeMerge(config, maliciousInput);
const newUser = {};
console.log(newUser.isAdmin); // true - prototype polluted
Safe practices include using Object.create(null) for prototype-less objects and validating inputs.
Prototype Chain Nature
The prototype chain is fundamentally a lookup mechanism rather than a specific data structure. It enables JavaScript's prototypal inheritance system by linking objects through their prototype references.