SECTION 1
Historical Context & Motivation
Most popular programming languages like Java and C++ use a system called classical inheritance, where you define a blueprint (called a class) and then create objects from it. JavaScript took a very different path. When Brendan Eich created JavaScript in 1995, he chose a model called prototypal inheritance, inspired by a lesser-known language called Self. Instead of classes serving as rigid blueprints, every JavaScript object can link directly to another object and borrow its properties. This decision made JavaScript uniquely flexible — and initially confusing for many developers.
1986 • Self Language Created
1995 • JavaScript Is Born
2009 • ECMAScript 5 & Object.create
Object.create(), making it easier to set up prototype chains directly and clearly.2015 • ES6 class Syntax
class keyword, but under the hood JavaScript still uses prototypes. Classes are simply a friendlier syntax — often called syntactic sugar.So here's the big question this lesson addresses: if JavaScript doesn't rely on traditional class blueprints, how do objects share behavior and avoid duplicating code? The answer lies in prototypes and the prototype chain — a clever linking system that lets one object look up properties from another when it doesn't have them itself.
SECTION 2
Core Principles & Definitions
Before diving into code, you need to understand four core ideas that make JavaScript's object system tick. These ideas work together to create a powerful mechanism for sharing functionality between objects.
Prototype
[[Prototype]] Link
__proto__ or Object.getPrototypeOf().Prototype Chain
null.Property Lookup
KEY TAKEAWAY
null).SECTION 3
Visual Explanation — The Prototype Chain
The diagram below shows three objects connected through the prototype chain. A dog object links to an animal object, which links to Object.prototype, which finally links to null. When you call dog.toString(), JavaScript can't find toString on dog or animal, so it walks up to Object.prototype where the method is defined.
[[Prototype]] references. The bottom panel demonstrates property lookup: when dog.speak() is called, JavaScript checks dog first, doesn't find it, then walks up the chain to animal where it succeeds.Notice that dog never needed to define speak() itself — it delegates the call up the chain. This is the fundamental magic of prototypes: objects can share methods without each one having its own copy, saving memory and keeping your code organized.
SECTION 4
How the Prototype Chain Works in Code
Let's look at exactly how JavaScript creates and navigates the prototype chain. There are a few key mechanisms you should understand: constructor functions with the .prototype property, Object.create(), and the property lookup algorithm.
Constructor Functions & .prototype
When you write a constructor function and call it with new, JavaScript automatically sets the new object's [[Prototype]] to point at the constructor's .prototype property. This is a bit confusing at first: every function in JavaScript has a .prototype property, and it's used as the template for objects created with new.
Don't Confuse These Two!
someFunction.prototype is a regular, visible property on a function that serves as the prototype for new objects. The hidden link [[Prototype]] (accessible via __proto__ or Object.getPrototypeOf()) is the internal link every object has that points to its parent in the chain.The Property Lookup Algorithm
Every time you access a property like obj.someProperty, JavaScript runs a simple search algorithm. It checks the object itself first. If the property isn't found, it follows the [[Prototype]] link to the next object and checks there. This continues until it either finds the property or reaches null at the end of the chain, at which point undefined is returned.
- Step 1: Check if the object has its own property with that name (an "own property").
- Step 2: If not found, follow
[[Prototype]]to the parent object and repeat Step 1. - Step 3: Continue until the property is found or
[[Prototype]]isnull. - Step 4: If the end of the chain is reached without finding the property, return
undefined.
SECTION 5
Anatomy of the Prototype Chain
To fully understand the prototype chain, it helps to see what happens when you create objects using different approaches. The diagram below compares three common scenarios: a plain object literal, an object created via a constructor function, and an array. Each one has a slightly different prototype chain, but they all end at the same place — Object.prototype and then null.
Object.prototype before reaching null. Arrays have an extra link through Array.prototype (which gives them methods like push() and map()), while constructor-created objects pass through the constructor's .prototype.This is why every JavaScript object has access to methods like toString() and hasOwnProperty() — they're defined on Object.prototype, which sits at the top of every prototype chain. Arrays additionally inherit array-specific methods from Array.prototype, and functions inherit from Function.prototype. The chain always flows upward and always ends at null.
| Object Type | Prototype Chain | Chain Length |
|---|---|---|
{ } | obj → Object.prototype → null | 2 links |
new Person() | instance → Person.prototype → Object.prototype → null | 3 links |
[1, 2, 3] | arr → Array.prototype → Object.prototype → null | 3 links |
Object.create(null) | obj → null | 1 link |
Common prototype chains and their lengths
SECTION 6
Worked Example — Building a Prototype Chain
Let's walk through creating a prototype chain step by step using Object.create() and then trace how JavaScript looks up a property.
Creating and Tracing a Prototype Chain
- 1
Step 1 — Define the Base Object
We start by creating a base object calledvehiclewith a shared method:const vehicle = { wheels: 4, start() { return 'Vroom!'; } };vehicle has two own properties:wheelsandstart. - 2
Step 2 — Create a Child Object with Object.create()
We useObject.create(vehicle)to create a new object whose[[Prototype]]points tovehicle:const car = Object.create(vehicle);car.brand = 'Toyota';car has one own property (brand) and inherits fromvehicle. - 3
Step 3 — Access an Inherited Property
Now let's accesscar.wheels. JavaScript first checkscar— does it havewheelsas an own property? No. So JavaScript follows the[[Prototype]]link tovehicleand checks there. Found it!car.wheelsreturns 4 (inherited from vehicle). - 4
Step 4 — Verify with hasOwnProperty()
We can confirm the difference between own and inherited properties:car.hasOwnProperty('brand') // truecar.hasOwnProperty('wheels') // falseThe methodhasOwnProperty()itself is inherited from Object.prototype — demonstrating the chain in action.brandis an own property;wheelsis inherited. - 5
Step 5 — Property Shadowing
What if we setcar.wheels = 3? This creates a new own property oncarthat shadows (hides) the inherited one. The originalvehicle.wheelsremains 4 — it is not modified.car.wheelsis now 3 (own);vehicle.wheelsis still 4.
SECTION 7
Prototypal vs. Classical Inheritance
If you've heard of object-oriented programming in languages like Java or C++, you may be used to classical inheritance where classes define structure and subclasses extend them. JavaScript's prototypal approach is fundamentally different, though the ES6 class syntax makes them look similar on the surface. Here's how they compare.
| Feature | Classical (Java, C++) | Prototypal (JavaScript) |
|---|---|---|
| Blueprint | Classes define structure at compile time | Objects link to other objects at runtime |
| Inheritance | Copies structure from parent class | Delegates lookups to prototype via chain |
| Flexibility | Fixed once compiled | Can modify prototypes at any time |
| Memory | Each object has its own copy of methods | Objects share methods via prototype |
| Syntax | class Dog extends Animal | Object.create(), constructors, or ES6 class (sugar) |
Classical inheritance vs. prototypal inheritance
KEY TAKEAWAY
SECTION 8
Connection to Advanced Concepts
Understanding prototypes is the foundation for many advanced JavaScript topics. The ES6 class keyword, for example, doesn't introduce a new inheritance model — it's just a cleaner way to set up prototype chains. Under the hood, class Dog extends Animal is doing the exact same thing as manually linking prototypes.
| What You Learn Now | What It Unlocks Next |
|---|---|
| Prototype chain and property lookup | Understanding how ES6 classes really work under the hood |
Object.create() | Composition over inheritance patterns and mixins |
| Property shadowing | Method overriding in class hierarchies and polymorphism |
Constructor functions and .prototype | The new keyword mechanics, instanceof operator |
How prototypes connect to advanced OOP concepts
As you advance, you'll also encounter mixins (combining behaviors from multiple sources), the delegation pattern (where objects explicitly hand off work to their prototypes), and design debates like "composition over inheritance." All of these build on the prototype foundation you're learning right now.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
Show answer
Question 2: BASIC CALCULATION
child.greet() return? Trace the prototype chain lookup.
const parent = { greet() { return 'Hello!'; } };
const child = Object.create(parent);
child.name = 'Alex';Show answer
child.greet() returns 'Hello!'. Here's the lookup trace: JavaScript first checks child for a greet property. It finds only name, so it follows the [[Prototype]] link to parent. There it finds greet() and executes it. The child never needed its own copy of the function — delegation handled everything.Question 3: INTERMEDIATE
a.x, b.x, and proto.x after all lines execute?
const proto = { x: 10 };
const a = Object.create(proto);
const b = Object.create(proto);
a.x = 20;Show answer
a.x is 20 because setting a.x = 20 creates a new own property on a that shadows the prototype's value. b.x is still 10 because b has no own x property and still delegates to proto. proto.x remains 10 — assigning to a.x never modifies the prototype itself.Question 4: APPLIED
character objects. All characters should share a takeDamage(amount) method that subtracts from their health property, but each character has their own name and health. Write the code using Object.create() to set this up efficiently. Explain why placing takeDamage on the prototype is better than putting it on every character object.Show answer
const characterProto = { takeDamage(amount) { this.health -= amount; } }; then const hero = Object.create(characterProto); hero.name = 'Aria'; hero.health = 100; Placing takeDamage on the prototype means all characters share a single copy of the function in memory, rather than each character storing its own copy. If you create 100 characters, you still only have one takeDamage function. When hero.takeDamage(25) is called, JavaScript finds the method on the prototype, but this still refers to hero, so each character's own health property is modified correctly.Question 5: CRITICAL THINKING
Object.create(null) creates an object with no prototype at all (its [[Prototype]] is null). What are the consequences of this? What built-in methods would you lose, and when might this actually be useful?Show answer
Object.create(null) has no link to Object.prototype, so it lacks methods like toString(), hasOwnProperty(), and valueOf(). Calling any of these would throw an error. This is useful when you need a truly "clean" dictionary or lookup table — since there are no inherited properties, you don't risk collisions with prototype method names. For example, if you used a regular object as a dictionary and someone stored a key called 'toString', it would shadow the built-in method and potentially cause bugs. With Object.create(null), every property is an own property, making it safe for arbitrary key-value storage.SUMMARY
Lesson Summary
Summary
Every JavaScript object has a hidden [[Prototype]] link that points to another object called its prototype. When you access a property on an object and it isn't found as an own property, JavaScript walks up the prototype chain — checking each linked object — until it either finds the property or reaches null. This process is called delegation, and it's how JavaScript objects share behavior without duplicating code.
Unlike classical inheritance where classes copy structure, JavaScript's prototypal inheritance creates live links between objects. You can set up chains using Object.create(), constructor functions with .prototype, or the ES6 class syntax (which is syntactic sugar over prototypes). When you assign a property to an object, it creates an own property that shadows the inherited one without modifying the prototype. All chains ultimately end at Object.prototype → null.