JavaScript’s Prototypal Inheritance Explained

JavaScript’s inheritance model is fundamentally different from class-based languages like Java or C# — objects inherit directly from other objects via a prototype chain, and class syntax is really just sugar on top of that.

const animal = {
    speak() {
        console.log(<code>${this.name} makes a sound.</code>);
    },
};

const dog = Object.create(animal); // dog's prototype IS animal
dog.name = 'Rex';
dog.speak(); // "Rex makes a sound." - found via the prototype chain

console.log(Object.getPrototypeOf(dog) === animal); // true

// class syntax: the SAME mechanism underneath, nicer syntax on top
class Animal {
    constructor(name) { this.name = name; }
    speak() { console.log(<code>${this.name} makes a sound.</code>); }
}
class Dog extends Animal {} // Dog.prototype's prototype is Animal.prototype

const rex = new Dog('Rex');
rex.speak(); // resolved via the exact same prototype chain lookup

When you call a method, JavaScript walks up the prototype chain until it finds a matching property — class syntax (added in ES6) didn’t change this mechanism at all, it just gave developers coming from class-based languages a more familiar syntax for the exact same underlying prototype behavior.