Hello! In our last lesson, we explored how call, apply, and bind work, and in building our polyfills, we attached them to Function.prototype to make them available on all functions. This naturally leads to a fundamental question: What exactly is this .prototype property, and how does it enable this kind of shared functionality?
This lesson dives into the very heart of JavaScript's object model to answer that. Our goal is to explain JavaScript's prototypal inheritance model and how the prototype chain is traversed for property lookup.
You'll discover that this is the core mechanism enabling code reuse and inheritance in JavaScript. Understanding it deeply is crucial, even when using modern ES6 classes, because classes are, as we'll see in the next lesson, an abstraction built directly on top of this prototypal system.
The Mystery of Inherited Properties
Let's start with a simple observation that seems to defy logic. Consider this object:
const user = {
name: 'Alex'
};
This object clearly has only one property: name. Yet, you can call a method like toString() on it without error:
console.log(user.toString()); // Outputs: "[object Object]"
Where did toString() come from? The user object doesn't define it. The answer lies in prototypal inheritance.
Every object in JavaScript has a hidden internal property, denoted in the ECMAScript specification as [[Prototype]], which is a link to another object. When you try to access a property that doesn't exist on the current object, the JavaScript engine follows this [[Prototype]] link to look for the property on the linked object. This linked object is called the prototype.
Prototypal inheritance - The Modern JavaScript Tutorial
The article 'Prototypal inheritance' from The Modern JavaScript Tutorial provides a clear introduction to this concept. It explains the [[Prototype]] link and how JavaScript uses it to find missing properties.
Read the first section, '[[Prototype]]'. Pay attention to how the __proto__ syntax is used in the examples to establish a prototype link between rabbit and animal, and how this allows rabbit to access properties from animal.
This creates a prototype chain: a series of linked objects that the engine traverses to resolve property lookups. This chain continues until it reaches an object whose [[Prototype]] is null, which marks the end of the chain.
The Property Lookup Algorithm
The process of accessing a property (e.g., myObject.myProp) follows a simple, deterministic algorithm:
- The engine checks if
myPropexists as an own property ofmyObject. - If yes, the value of that property is returned.
- If no, the engine follows the
[[Prototype]]link tomyObject's prototype. - It repeats step 1 on the prototype object.
- This process continues up the prototype chain until the property is found or the end of the chain (
null) is reached. - If the end of the chain is reached without finding the property,
undefinedis returned.
To see this process in action, the following video segment provides an excellent visual and code-based walkthrough.
Prototypes and Prototypal Inheritance - Javascript In Depth
This video from 'Tech with Nader' demonstrates the lookup process, showing what happens when a property is found on the object itself, its prototype, or not at all.
Watch the segment from 19:53 to 21:46. The visual diagram clearly illustrates the step-by-step traversal up the prototype chain when looking for the speak method.
It's important to note that this chain is only for reading properties. When you write to a property (myObject.myProp = 'someValue'), you are always creating or modifying an own property on myObject itself. This is called property shadowing—the new own property "shadows" any property of the same name that might exist further up the prototype chain.
[[Prototype]] vs. .prototype: The Core Distinction
Here we arrive at one of the most common points of confusion in JavaScript. We have two very similar-looking terms that mean different things:
[[Prototype]]: An internal, hidden property on nearly all objects. It is the link to the next object in the prototype chain. It can be accessed viaObject.getPrototypeOf(obj)..prototype: A regular property that exists on all functions (except arrow functions). It's just a plain object. Its sole purpose is to become the[[Prototype]]of any new objects created when that function is used as a constructor with thenewkeyword.
Let's make this concrete.
// A constructor function
function User(name) {
this.name = name;
}
// User.prototype is an object. We can add shared methods to it.
User.prototype.sayHi = function() {
console.log(`Hi, I'm ${this.name}`);
};
// When we use 'new', we create a new object `newUser`.
const newUser = new User('Sasha');
// The JS engine does something crucial behind the scenes:
// It sets newUser's [[Prototype]] to be User.prototype.
// Object.getPrototypeOf(newUser) === User.prototype; // true
// Now, newUser can access the sayHi method.
newUser.sayHi(); // Hi, I'm Sasha
The sayHi method is found by traversing newUser's [[Prototype]] link to the User.prototype object.
This diagram is extremely helpful for visualizing these distinct links.

The Behavior of this in Inherited Methods
As we saw in the previous lesson, the value of this is determined at call time. This rule holds true for inherited methods.
No matter where a method is found in the prototype chain, the this keyword inside that method always refers to the object that initiated the call (the object before the dot).
This is a powerful feature. It allows objects to share methods from a prototype but operate on their own unique state.
Prototypal inheritance - The Modern JavaScript Tutorial
The 'Prototypal inheritance' tutorial has an excellent section explaining this concept. Pay close attention to the rabbit.sleep() example.
Read the section titled 'The value of "this"'. Notice how calling rabbit.sleep() modifies rabbit.isSleeping, not animal.isSleeping. The method is shared, but the state is not.
A Common Pitfall: Shared Mutable State
While sharing methods is efficient, sharing properties that are mutable reference types (like arrays or objects) can lead to unintended side effects. If a property on the prototype is an object, all instances will share a reference to that single object.
The "hamster stomach" problem in the next reading is a classic illustration of this bug.
Prototypal inheritance - The Modern JavaScript Tutorial
Let's examine a common error and how to fix it. This task will solidify your understanding of how this and property assignment work with prototypes.
Read the task 'Why are both hamsters full?'. Understand why modifying this.stomach via .push() affects all instances and why creating a unique stomach for each instance is the correct solution.
The "Pure" Prototypal Way: Object.create()
While constructor functions and the new keyword are the traditional way to leverage prototypes, some argue that they obscure the true nature of prototypal inheritance by mimicking classical inheritance.
The modern Object.create(proto) method provides a more direct and "pure" way to create an object with a specified prototype. It creates a new, empty object whose [[Prototype]] is set to the proto object you pass in.
const personPrototype = {
greet() {
return `Hello, my name is ${this.firstName}`;
}
};
const jared = Object.create(personPrototype);
jared.firstName = 'Jared';
console.log(jared.greet()); // "Hello, my name is Jared"
// We can build chains
const employeePrototype = Object.create(personPrototype);
employeePrototype.getJobTitle = function() {
return `My job is ${this.jobTitle}`;
}
const ben = Object.create(employeePrototype);
ben.firstName = 'Ben';
ben.jobTitle = 'Developer';
console.log(ben.greet()); // "Hello, my name is Ben" (from personPrototype)
console.log(ben.getJobTitle()); // "My job is Developer" (from employeePrototype)
// You can inspect the chain
console.log(Object.getPrototypeOf(ben) === employeePrototype); // true
console.log(Object.getPrototypeOf(employeePrototype) === personPrototype); // true
This approach makes the inheritance chain explicit without the indirection of constructor functions.
Source Decoded 3: Javascript -- Prototypes, Prototypal Inheritance done right.
The video 'Source Decoded 3' offers a compelling argument for using Object.create() as the 'correct' way to implement prototypal inheritance, contrasting it with the confusion that can arise from new and constructors.
Watch from 29:50 to 39:11. The presenters demonstrate how to build clean, multi-level prototype chains using Object.create(). This provides a powerful alternative mental model to the constructor-based approach.
Test Your Understanding
Let's test your knowledge with a few quick exercises based on the concepts we've covered. Try to predict the output before running the code.
Exercise 1: Property Lookup & Deletion
let animal = {
jumps: null
};
let rabbit = {
__proto__: animal,
jumps: true
};
console.log( rabbit.jumps ); // ? (1)
delete rabbit.jumps;
console.log( rabbit.jumps ); // ? (2)
delete animal.jumps;
console.log( rabbit.jumps ); // ? (3)
Click for answers
1. `true` (from `rabbit`'s own property) 2. `null` (from `animal`'s property, after `rabbit`'s was deleted) 3. `undefined` (the property no longer exists anywhere on the chain)Exercise 2: Where Does it Write?
let animal = {
eat() {
this.full = true;
}
};
let rabbit = {
__proto__: animal
};
rabbit.eat();
// Which object now has the 'full' property? animal or rabbit?
console.log(rabbit.hasOwnProperty('full')); // ?
console.log(animal.hasOwnProperty('full')); // ?
Click for answers
`rabbit`. The method was *found* on `animal`, but `this` during the call was `rabbit`, so the property `full` was written directly onto the `rabbit` object. - `console.log(rabbit.hasOwnProperty('full'));` will be `true`. - `console.log(animal.hasOwnProperty('full'));` will be `false`.Conclusion
We've peeled back the layers of JavaScript's inheritance model, moving from a simple mystery to a deep, mechanical understanding.
Key Takeaways:
- Prototype Chain: Objects are linked via an internal
[[Prototype]]property, forming a chain that is traversed during property lookups. - Lookup Algorithm: The engine checks for an "own" property first, then moves up the prototype chain until the property is found or the chain ends at
null. [[Prototype]]vs..prototype:[[Prototype]]is the internal link on an object. A function's.prototypeproperty is the object that becomes the[[Prototype]]for instances created withnew.thisis Dynamic: An inherited method, when called, has itsthisvalue set to the instance calling it, not the prototype where the method resides.Object.create(): This provides a direct, clean mechanism for setting an object's prototype at creation, embodying a "purer" form of prototypal inheritance.
What's Next?
Now that you have a solid grasp of how prototypes work under the hood, you are perfectly prepared for our next lesson. We will explore ES6 classes and you will see how the class, extends, and super keywords are essentially "syntactic sugar" that provides a cleaner, more familiar syntax for managing the very same prototype chains we've just demystified.