Prototypes and Inheritance
Understanding prototype chain and object-oriented programming in JavaScript
Challenge
Section titled “Challenge”You need to create a hierarchy of objects (Animal → Dog) where Dog inherits properties and methods from Animal. Why does modifying a prototype affect all instances?
Explanation
Section titled “Explanation”JavaScript uses prototype-based inheritance instead of classical inheritance. Every object has a prototype, and objects inherit properties through the prototype chain.
Prototype Basics
Section titled “Prototype Basics”function Animal(name) { this.name = name;}
// Add method to prototype (shared across instances)Animal.prototype.speak = function() { console.log(`${this.name} makes a sound`);};
const dog = new Animal('Rex');dog.speak(); // "Rex makes a sound"
console.log(dog.hasOwnProperty('speak')); // false - method is on prototypeconsole.log('speak' in dog); // true - found via prototype chainPrototype Chain
Section titled “Prototype Chain”function Animal(name) { this.name = name;}Animal.prototype.type = 'Animal';
function Dog(name, breed) { Animal.call(this, name); // Call parent constructor this.breed = breed;}
// Set up inheritanceDog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() { console.log('Woof!');};
const myDog = new Dog('Buddy', 'Golden Retriever');console.log(myDog.name); // "Buddy" - own propertyconsole.log(myDog.breed); // "Golden Retriever" - own propertyconsole.log(myDog.type); // "Animal" - inherited from Animal.prototypemyDog.bark(); // "Woof!"
// Prototype chain: myDog -> Dog.prototype -> Animal.prototype -> Object.prototype -> nullInspecting the Prototype Chain
Section titled “Inspecting the Prototype Chain”function Animal(name) { this.name = name;}Animal.prototype.move = function() {};
function Dog(name) { Animal.call(this, name);}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.bark = function() {};
const dog = new Dog('Max');
// Different ways to checkconsole.log(dog instanceof Dog); // trueconsole.log(dog instanceof Animal); // trueconsole.log(dog instanceof Object); // true
console.log(Dog.prototype.isPrototypeOf(dog)); // trueconsole.log(Animal.prototype.isPrototypeOf(dog)); // true
console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true
// View entire chainconsole.log(Object.getPrototypeOf(dog)); // Dog.prototypeconsole.log(Object.getPrototypeOf(Object.getPrototypeOf(dog))); // Animal.prototypeconsole.log(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(dog)))); // Object.prototypeModern ES6 Class Syntax
Section titled “Modern ES6 Class Syntax”// ES6 classes are syntactic sugar over prototypes
class Animal { constructor(name) { this.name = name; }
speak() { console.log(`${this.name} makes a sound`); }}
class Dog extends Animal { constructor(name, breed) { super(name); // Call parent constructor this.breed = breed; }
speak() { super.speak(); // Call parent method console.log('Woof!'); }
bark() { console.log('Bark bark bark!'); }}
const dog = new Dog('Rex', 'Labrador');dog.speak(); // "Rex makes a sound" \n "Woof!"dog.bark(); // "Bark bark bark!"Property Lookup Chain
Section titled “Property Lookup Chain”function Person() { this.age = 30;}Person.prototype.name = 'Generic Person';Person.prototype.work = function() {};
const john = new Person();john.name = 'John'; // Own property shadows prototype
console.log(john.name); // "John" - found on john itself firstconsole.log(john.age); // 30 - found on johnconsole.log(john.walk); // undefined - not in chain
delete john.name; // Remove own propertyconsole.log(john.name); // "Generic Person" - now looks up to prototype
for (let key in john) { console.log(key); // age, name, work - includes inherited properties}
for (let key of Object.keys(john)) { console.log(key); // age - only own properties}Potential Issues and Solutions
Section titled “Potential Issues and Solutions”// ⚠️ Problem: Shared mutable reference on prototypefunction Config() {}Config.prototype.settings = []; // Shared array!
const config1 = new Config();const config2 = new Config();
config1.settings.push('setting1');console.log(config2.settings); // ['setting1'] - ❌ Unexpected!
// ✅ Solution: Use constructor for mutable objectsfunction GoodConfig() { this.settings = []; // Each instance gets own array}
const goodConfig1 = new GoodConfig();const goodConfig2 = new GoodConfig();
goodConfig1.settings.push('setting1');console.log(goodConfig2.settings); // [] - ✅ IsolatedInterview Tip: instanceof and Type Checking
Section titled “Interview Tip: instanceof and Type Checking”class Animal {}class Dog extends Animal {}
const dog = new Dog();
console.log(dog instanceof Dog); // trueconsole.log(dog instanceof Animal); // true
// Safer: check for constructorconsole.log(dog.constructor === Dog); // true
// Safest: use Symbol.toStringTag or duck typingconsole.log(Object.prototype.toString.call(dog)); // [object Object]