Technology · JavaScript
AdvancedJavaScript Prototypal Inheritance
Understand the prototype chain, Object.create, and how class syntax wraps prototypal inheritance.
TL;DR
- 01Objects inherit properties through a linked chain of prototypes.
- 02Use Object.create to set an object's prototype directly.
- 03Class syntax is sugar over the same prototype mechanism.
The Prototype Chain
- Every object links to another object called its prototype, forming a chain.
const animal = { eats: true }; const rabbit = Object.create(animal); rabbit.hops = true; rabbit.eats; // true — found via the prototype chain - Property lookups walk up the chain until the property is found or the chain ends.
- The chain ends at
Object.prototype, whose own prototype isnull. - Methods like
.toString()come fromObject.prototype, inherited by nearly everything. - Writing a property always creates it on the object itself, never on the prototype.
- Reading a missing property returns
undefinedonly after the whole chain is checked.
Creating and Inspecting Prototypes
- Use
Object.create(proto)to make a new object with a specific prototype directly.const base = { greet() { return 'hi'; } }; const obj = Object.create(base); obj.greet(); // 'hi' - Pass
nulltoObject.create(null)to build an object with no prototype at all. - Use
Object.getPrototypeOf(obj)to read an object's current prototype safely.Object.getPrototypeOf(obj) === base; // true - Use
Object.setPrototypeOf(obj, proto)to change a prototype after creation. - Avoid
__proto__in new code; it works but is a legacy accessor, not a clean API. Object.createis the most direct way to model inheritance without constructors.
Constructor Functions and prototype
- Every function has a
prototypeproperty used as the prototype for objects it constructs.function Dog(name) { this.name = name; } Dog.prototype.bark = function () { return `${this.name} barks`; }; const rex = new Dog('Rex'); rex.bark(); // 'Rex barks' - Calling a function with
newcreates an object linked to that function'sprototype. - Methods defined on
prototypeare shared by every instance, saving memory. - Instance-specific data, like
name, belongs onthisinside the constructor, not the prototype. - Check
rex instanceof Dogto confirmDog.prototypeis inrex's prototype chain. - This pattern is the original way JavaScript implemented inheritance before
class.
Class Syntax as Prototype Sugar
- A
classdeclaration creates a constructor function with methods on its prototype, just like the manual pattern.class Dog { constructor(name) { this.name = name; } bark() { return `${this.name} barks`; } } typeof Dog; // 'function' extendssets up the prototype chain between parent and child classes automatically.class Puppy extends Dog { bark() { return `${super.bark()} (puppy style)`; } }super.method()inside a subclass calls the parent's version via the prototype chain.- Class methods are non-enumerable by default, unlike properties added with plain assignment.
- Class bodies run in strict mode and are not hoisted like function declarations.
- Despite the new syntax,
Puppy.prototypeis still a plain object linked toDog.prototype.
Own Properties vs Shadowing
- An own property lives directly on the object; an inherited one lives on its prototype.
const base = { color: 'red' }; const item = Object.create(base); item.hasOwnProperty('color'); // false - Assigning to
item.colorcreates a new own property that shadows the inherited one.item.color = 'blue'; item.color; // 'blue' — own property wins base.color; // 'red' — unchanged - Use
Object.hasOwn(obj, key)as the modern, safer alternative tohasOwnProperty(). - Shadowing never deletes or modifies the prototype's original property.
- Use
delete item.colorto remove the own property and reveal the inherited one again. for...inenumerates inherited properties too; filter withhasOwnProperty()when needed.
Tips
- 01Use Object.getPrototypeOf() and Object.setPrototypeOf() instead of __proto__, since they are the standardized and faster API.
- 02Call hasOwnProperty() when you need to check a property's own existence without accidentally matching an inherited one.
Warnings
- 01Setting __proto__ repeatedly deoptimizes property access in most JavaScript engines, so prefer Object.create at creation time.
- 02A for...in loop walks inherited enumerable properties too, so unfiltered loops can leak prototype methods into your iteration unexpectedly.
FAQ
- Every object has an internal link to another object, its prototype. Reading a property checks the object first, then walks up each prototype until it finds a match or reaches null. This chain is how objects share methods without copying them.
- __proto__ is a legacy accessor property that exposes an object's prototype and predates standardization. Object.getPrototypeOf() and Object.setPrototypeOf() are the modern, standardized equivalents. Use the function forms in real code; __proto__ remains mostly for compatibility.
- No — a class declaration creates a constructor function with methods placed on its prototype object. This works just like the older function-based pattern. Class syntax adds strictness, cleaner inheritance with extends, and no hoisting of the body. Under the hood, instances still link through the same prototype chain.
- Shadowing happens when an object defines its own property with the same name as one on its prototype. The own property wins, and lookups never reach the prototype's version for that name. The prototype's property still exists; it is just hidden for that one object.
- Call obj.hasOwnProperty('name') to check if the property exists directly on the object itself. It returns false for properties found only further up the prototype chain. This distinguishes an object's own data from methods or fields it inherits.