JavaScript Prototypes - Complete Guide
Table of Contentsโ
- What is Prototype in JavaScript?
__proto__vsprototype- The
newKeyword - Internals Explained - Prototype Chain - Lookup Mechanism
- Visual Prototype Chain
- Inheritance Using Constructor Functions
Object.create()- Pure Prototypal Inheritance- ES6 Classes - Syntactic Sugar
- Prototype vs Instance Members
hasOwnPropertyandinOperator- Modifying Prototypes - Dangers
__proto__vsObject.getPrototypeOf()- Built-in Prototype Chains
- Common Interview Traps
- Prototype Chain vs Scope Chain
Object.create()vsnew- Performance of Prototype Lookup
- How
instanceofWorks Internally - Prototype Pitfalls in Real Apps
- Advanced Interview Questions
What is Prototype in JavaScript?โ
JavaScript uses prototypal inheritance, not classical inheritance like Java or C++.
Key Concept:โ
Every object in JavaScript has a hidden internal property called [[Prototype]]. This property forms the prototype chain that enables inheritance.
Core Principle:โ
Objects inherit properties and methods from other objects, not from classes.
Internal Property:โ
const obj = {};
// obj has a hidden [[Prototype]] property
// You cannot directly access [[Prototype]]
// But you can access it via __proto__ or Object.getPrototypeOf()
__proto__ vs prototypeโ
This is the MOST IMPORTANT and MOST CONFUSING concept in JavaScript prototypes.
__proto__ (Dunder Proto)โ
- Property on: Every object instance
- Points to: The object's
[[Prototype]] - Used for: Property lookup in the prototype chain
- Type: Accessor property (getter/setter)
const obj = {};
console.log(obj.__proto__); // Object.prototype
prototypeโ
- Property on: Constructor functions only
- Purpose: Blueprint for instances created with
new - Used for: Defining shared methods and properties
- Type: Regular object
function User() {}
User.prototype.sayHi = function() {
console.log('Hi!');
};
const user = new User();
The Golden Rule (Memorize This):โ
object.__proto__ === Constructor.prototype
Visual Comparison:โ
function User(name) {
this.name = name;
}
User.prototype.greet = function() {
console.log(`Hello, ${this.name}`);
};
const user1 = new User('Alice');
// The relationship:
user1.__proto__ === User.prototype // true
Relationship Diagram:โ
Constructor Function (User)
|
| Has property
โ
User.prototype (object)
โ
| Points to (__proto__)
|
user1 (instance)
The new Keyword - Internals Explainedโ
Understanding what happens when you use new is crucial for understanding prototypes.
Example:โ
function User(name) {
this.name = name;
}
const u = new User('Pawan');
What new Does Internally (4 Steps):โ
- Creates an empty object:
const obj = {} - Sets prototype link:
obj.__proto__ = User.prototype - Executes constructor:
User.call(obj, 'Pawan')(bindsthistoobj) - Returns the object: Returns
obj(unless constructor explicitly returns an object)
Manual Implementation of new:โ
function myNew(Constructor, ...args) {
// Step 1: Create empty object
const obj = {};
// Step 2: Set prototype link
obj.__proto__ = Constructor.prototype;
// Or: Object.setPrototypeOf(obj, Constructor.prototype);
// Step 3: Execute constructor with obj as 'this'
const result = Constructor.apply(obj, args);
// Step 4: Return object (or constructor's return value if it's an object)
return typeof result === 'object' && result !== null ? result : obj;
}
// Usage:
function User(name) {
this.name = name;
}
const user = myNew(User, 'Alice');
console.log(user.name); // 'Alice'
Important Edge Case:โ
function User() {
this.name = 'Alice';
return { custom: 'object' }; // Explicit object return
}
const u = new User();
console.log(u); // { custom: 'object' }
// The 'this.name' is ignored because constructor returns an object
Prototype Chain - Lookup Mechanismโ
When you access a property on an object, JavaScript follows a specific lookup order.
Example:โ
function User(name) {
this.name = name;
}
User.prototype.sayHi = function() {
console.log(`Hi, ${this.name}`);
};
const u = new User('Pawan');
u.sayHi(); // Where does JS find sayHi()?
Lookup Order:โ
1. u (own properties) โ not found
โ
2. u.__proto__ (User.prototype) โ
found sayHi
Full Chain Example:โ
u.toString(); // Where is toString()?
Lookup order:
1. u (own properties) โ not found
โ
2. u.__proto__ (User.prototype) โ not found
โ
3. User.prototype.__proto__ (Object.prototype) โ
found toString
When Property is Not Found:โ
u.nonExistent; // undefined
// Chain:
u โ u.__proto__ โ Object.prototype โ null
// Reaches null, returns undefined
Key Rules:โ
- Search stops at first match
- Search ends at
null - Returns
undefinedif not found
Visual Prototype Chainโ
Understanding the visual structure helps tremendously in interviews.
Basic Chain:โ
u (instance)
|
โโโ __proto__ โ User.prototype
|
โโโ __proto__ โ Object.prototype
|
โโโ __proto__ โ null
Detailed Example:โ
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('Woof!');
};
const dog = new Dog('Buddy', 'Golden Retriever');
Chain Visualization:โ
dog
|
โโโ __proto__ โ Dog.prototype
|
โโโ bark() โ
โโโ constructor: Dog
|
โโโ __proto__ โ Animal.prototype
|
โโโ eat() โ
|
โโโ __proto__ โ Object.prototype
|
โโโ toString() โ
โโโ hasOwnProperty() โ
|
โโโ __proto__ โ null
Inheritance Using Constructor Functionsโ
This is the pre-ES6 way of implementing inheritance.
Complete Example:โ
// Parent Constructor
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a sound`);
};
// Child Constructor
function Dog(name, breed) {
// Call parent constructor to inherit instance properties
Animal.call(this, name);
this.breed = breed;
}
// Set up prototype chain
Dog.prototype = Object.create(Animal.prototype);
// Fix constructor reference
Dog.prototype.constructor = Dog;
// Add child-specific methods
Dog.prototype.bark = function() {
console.log(`${this.name} barks`);
};
// Usage
const dog = new Dog('Tommy', 'Labrador');
dog.speak(); // 'Tommy makes a sound' (inherited)
dog.bark(); // 'Tommy barks' (own method)
Why Object.create()?โ
// โ WRONG - Calls parent constructor
Dog.prototype = new Animal();
// โ
CORRECT - Creates clean prototype link without calling constructor
Dog.prototype = Object.create(Animal.prototype);
Advantages of Object.create():
- Creates a clean prototype link
- Avoids calling the parent constructor
- No unwanted instance properties on prototype
Why Fix constructor?โ
Dog.prototype = Object.create(Animal.prototype);
console.log(Dog.prototype.constructor); // Animal โ
Dog.prototype.constructor = Dog;
console.log(Dog.prototype.constructor); // Dog โ
Importance:
- Maintains proper constructor reference
- Helps with debugging
- Required for some reflection operations
Object.create() - Pure Prototypal Inheritanceโ
Object.create() enables pure prototypal inheritance without constructor functions.
Syntax:โ
Object.create(proto, [propertiesObject])
Basic Example:โ
const parent = {
greet() {
console.log('Hello from parent');
}
};
const child = Object.create(parent);
child.greet(); // 'Hello from parent'
console.log(child.__proto__ === parent); // true
Creating Objects with Properties:โ
const parent = {
greet() {
return `Hello, ${this.name}`;
}
};
const child = Object.create(parent, {
name: {
value: 'Alice',
writable: true,
enumerable: true,
configurable: true
}
});
console.log(child.greet()); // 'Hello, Alice'
Creating Object with No Prototype:โ
const obj = Object.create(null);
console.log(obj.__proto__); // undefined
console.log(obj.toString); // undefined
// Useful for creating pure dictionaries/maps
ES6 Classes - Syntactic Sugarโ
ES6 classes are just syntactic sugar over the prototype system.
Class Syntax:โ
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Calls parent constructor
this.breed = breed;
}
bark() {
console.log(`${this.name} barks`);
}
}
const dog = new Dog('Max', 'Husky');
dog.speak(); // 'Max makes a sound'
dog.bark(); // 'Max barks'
What Happens Internally:โ
// Under the hood, this is still:
Dog.prototype.__proto__ === Animal.prototype // true
dog.__proto__ === Dog.prototype // true
Class vs Constructor Function:โ
| Feature | Class | Constructor Function |
|---|---|---|
| Syntax | Cleaner | More verbose |
| Hoisting | Not hoisted | Hoisted |
| Strict mode | Always strict | Optional |
new required | Yes, throws error | No, but recommended |
| Prototype | Same mechanism | Same mechanism |
Important: Classes are NOT Real Classesโ
typeof Animal // 'function' โ
Animal.prototype.constructor === Animal // true
JavaScript still uses prototypes under the hood!
Prototype vs Instance Membersโ
Understanding where to place properties is crucial for memory efficiency.
Instance Properties:โ
function User(name) {
this.name = name; // Instance property (unique per object)
this.greet = function() { // Instance method (duplicated!)
console.log(`Hi, ${this.name}`);
};
}
const u1 = new User('Alice');
const u2 = new User('Bob');
console.log(u1.greet === u2.greet); // false โ Different functions!
Prototype Properties:โ
function User(name) {
this.name = name; // Instance property (unique per object)
}
User.prototype.greet = function() { // Shared method โ
console.log(`Hi, ${this.name}`);
};
const u1 = new User('Alice');
const u2 = new User('Bob');
console.log(u1.greet === u2.greet); // true โ
Same function!
Memory Comparison:โ
// โ BAD - 1000 function copies in memory
for (let i = 0; i < 1000; i++) {
const user = new User('User' + i);
// Each has its own greet function
}
// โ
GOOD - 1 shared function in memory
User.prototype.greet = function() { /*...*/ };
for (let i = 0; i < 1000; i++) {
const user = new User('User' + i);
// All share the same greet function
}
Best Practice:โ
Instance properties โ Constructor
Shared methods โ Prototype
hasOwnProperty and in Operatorโ
These methods help distinguish between own and inherited properties.
hasOwnProperty():โ
Checks if property exists directly on the object (not in prototype chain).
function User(name) {
this.name = name;
}
User.prototype.greet = function() {};
const user = new User('Alice');
console.log(user.hasOwnProperty('name')); // true โ
console.log(user.hasOwnProperty('greet')); // false โ (in prototype)
console.log(user.hasOwnProperty('toString')); // false โ
in Operator:โ
Checks if property exists anywhere in the prototype chain.
console.log('name' in user); // true โ
console.log('greet' in user); // true โ
(in prototype)
console.log('toString' in user); // true โ
(in Object.prototype)
console.log('fake' in user); // false โ
Comparison Table:โ
| Method | Own Properties | Inherited Properties |
|---|---|---|
hasOwnProperty() | โ Yes | โ No |
in operator | โ Yes | โ Yes |
Iterating Own Properties:โ
const user = new User('Alice');
// Only own enumerable properties
for (let key in user) {
if (user.hasOwnProperty(key)) {
console.log(key, user[key]);
}
}
// Modern approach
Object.keys(user); // ['name'] - only own enumerable
Object.getOwnPropertyNames(user); // ['name'] - all own properties
Modifying Prototypes - Dangersโ
Modifying prototypes affects all existing and future instances.
Example:โ
function User(name) {
this.name = name;
}
const u1 = new User('Alice');
const u2 = new User('Bob');
// Modify prototype AFTER instances created
User.prototype.age = 30;
console.log(u1.age); // 30 โ ๏ธ Both affected!
console.log(u2.age); // 30 โ ๏ธ
Dangers:โ
// โ DANGEROUS - Modifying built-in prototypes
Array.prototype.first = function() {
return this[0];
};
[1, 2, 3].first(); // 1
// Problems:
// 1. Affects ALL arrays globally
// 2. Can break third-party libraries
// 3. Future JS versions might add same method
// 4. Pollutes for...in loops
Safe Approach:โ
// โ
GOOD - Use utility functions instead
function first(arr) {
return arr[0];
}
first([1, 2, 3]); // 1
When Prototype Modification is OK:โ
// Polyfills for older browsers
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement) {
return this.indexOf(searchElement) !== -1;
};
}
__proto__ vs Object.getPrototypeOf()โ
While __proto__ works, it's not the recommended way to access prototypes.
__proto__ (Deprecated Pattern):โ
const obj = {};
console.log(obj.__proto__); // Object.prototype
// Setting prototype
const parent = { x: 10 };
const child = {};
child.__proto__ = parent;
Issues:
- Not part of ES6 standard (added for compatibility)
- Performance issues
- Deprecated in favor of modern methods
Recommended Modern Approach:โ
// Get prototype
const proto = Object.getPrototypeOf(obj);
// Set prototype
const parent = { x: 10 };
const child = {};
Object.setPrototypeOf(child, parent);
// Or better: use Object.create()
const child = Object.create(parent);
Performance Note:โ
// โ BAD - Changing prototype after creation is SLOW
const obj = {};
Object.setPrototypeOf(obj, parent);
// โ
GOOD - Set prototype during creation
const obj = Object.create(parent);
Comparison Table:โ
| Method | Status | Use Case |
|---|---|---|
__proto__ | Deprecated | Avoid in production |
Object.getPrototypeOf() | โ Recommended | Reading prototype |
Object.setPrototypeOf() | โ ๏ธ Use carefully | Changing prototype (slow) |
Object.create() | โ Recommended | Creating with prototype |
Built-in Prototype Chainsโ
Understanding built-in prototype chains helps with debugging and advanced usage.
Array Prototype Chain:โ
const arr = [1, 2, 3];
arr
โโโ __proto__ โ Array.prototype
|
โโโ map() โ
โโโ filter() โ
โโโ push() โ
|
โโโ __proto__ โ Object.prototype
|
โโโ toString() โ
โโโ __proto__ โ null
console.log(arr.__proto__ === Array.prototype); // true
console.log(Array.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
Function Prototype Chain:โ
function myFunc() {}
myFunc
โโโ __proto__ โ Function.prototype
|
โโโ call() โ
โโโ apply() โ
โโโ bind() โ
|
โโโ __proto__ โ Object.prototype
|
โโโ __proto__ โ null
String Prototype Chain:โ
const str = "hello";
str (primitive)
|
โโโ Wrapped temporarily as String object
|
โโโ __proto__ โ String.prototype
|
โโโ toUpperCase() โ
โโโ slice() โ
|
โโโ __proto__ โ Object.prototype
All Built-in Objects Eventually Lead to Object.prototype:โ
Any Object โ SpecificType.prototype โ Object.prototype โ null
Common Interview Trapsโ
Trap 1: Arrow Functions and Prototypeโ
const User = (name) => {
this.name = name;
};
User.prototype.greet = function() {}; // undefined!
const u = new User('Alice'); // โ TypeError: User is not a constructor
Why?
- Arrow functions don't have
prototypeproperty - Arrow functions cannot be used with
new - Arrow functions don't have their own
this
Trap 2: Changing Prototype After Object Creationโ
function User() {}
const u1 = new User();
// Change prototype
User.prototype = { newMethod() {} };
const u2 = new User();
console.log(u1.newMethod); // undefined โ (points to old prototype)
console.log(u2.newMethod); // function โ
(points to new prototype)
Lesson: Existing instances maintain reference to old prototype.
Trap 3: Why Methods Go on Prototype?โ
// โ BAD - Creates function for each instance
function User(name) {
this.greet = function() {
console.log(name);
};
}
// 1000 instances = 1000 function copies โ
// โ
GOOD - Shared method
User.prototype.greet = function() {
console.log(this.name);
};
// 1000 instances = 1 function โ
Benefits:
- Memory efficiency - shared across all instances
- Faster object creation - less work per instance
- Easier updates - modify once, affects all instances
Trap 4: Forgetting constructor Fixโ
function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype);
// Forgot: Child.prototype.constructor = Child;
const c = new Child();
console.log(c.constructor); // Parent โ Wrong!
console.log(c.constructor === Child); // false โ
Trap 5: Prototype Property Shadowingโ
User.prototype.role = 'user';
const u1 = new User();
console.log(u1.role); // 'user' (from prototype)
u1.role = 'admin'; // Creates OWN property
console.log(u1.role); // 'admin' (own property shadows prototype)
delete u1.role;
console.log(u1.role); // 'user' (back to prototype)
Prototype Chain vs Scope Chainโ
These are completely different concepts that students often confuse.
| Aspect | Prototype Chain | Scope Chain |
|---|---|---|
| Purpose | Property/method lookup | Variable lookup |
| Created | Runtime (object creation) | Definition time (lexical) |
| Basis | __proto__ links | Nested functions |
| Lookup | this-based | Lexical scope |
| Related to | Objects and inheritance | Functions and closures |
| Ends at | null | Global scope |
Example Comparison:โ
const parent = {
x: 10,
getX() {
return this.x;
}
};
const child = Object.create(parent);
child.x = 20;
function outer() {
const x = 30;
function inner() {
console.log(x); // 30 (scope chain)
console.log(child.getX()); // 20 (prototype chain + this)
}
inner();
}
outer();
Explanation:
xininner()โ scope chain lookup โ finds 30 inouter()child.getX()โ prototype chain lookup โ finds method inparentthis.xinsidegetX()โthisrefers tochild, so returns 20
Object.create() vs newโ
Both create objects but work differently.
Using new:โ
function User(name) {
this.name = name;
}
User.prototype.greet = function() {
console.log(`Hi, ${this.name}`);
};
const user = new User('Alice');
What happens:
- Creates object
- Links to
User.prototype - Runs constructor with passed arguments
- Returns object
Using Object.create():โ
const userPrototype = {
greet() {
console.log(`Hi, ${this.name}`);
}
};
const user = Object.create(userPrototype);
user.name = 'Alice'; // Manual assignment
What happens:
- Creates object
- Links to specified prototype
- Does NOT run any constructor
- Returns object
Comparison Table:โ
| Feature | new Constructor() | Object.create(proto) |
|---|---|---|
| Runs constructor | โ Yes | โ No |
| Sets prototype | โ
Yes (Constructor.prototype) | โ Yes (specified proto) |
| Passes arguments | โ Yes | โ No |
| Use case | Constructor pattern | Pure prototypal inheritance |
| Initialization | Automatic | Manual |
When to Use Each:โ
Use new:
- When you have a constructor function
- When you need initialization logic
- When following constructor pattern
Use Object.create():
- When you want pure prototypal inheritance
- When setting up inheritance chains
- When you need an object with specific prototype
Combining Both:โ
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {};
function Dog(name) {
Animal.call(this, name); // Borrow constructor
}
Dog.prototype = Object.create(Animal.prototype); // Set prototype
Dog.prototype.constructor = Dog;
const dog = new Dog('Max'); // Use new for final object
Performance of Prototype Lookupโ
Prototype lookup has performance implications in real applications.
Lookup Cost:โ
const obj = {
a: 1
};
// Fast - own property (1 lookup)
console.log(obj.a);
// Slower - prototype property (2 lookups)
console.log(obj.toString);
// Even slower - deep in chain (3+ lookups)
obj.__proto__.__proto__.hasOwnProperty;
Lookup Speed:โ
Own property: FAST โก (1 lookup)
Prototype: MEDIUM ๐ข (2 lookups)
Deep prototype: SLOW ๐ (3+ lookups)
Performance Best Practices:โ
1. Cache Prototype Properties:โ
// โ SLOW - Repeated prototype lookups
for (let i = 0; i < 1000000; i++) {
obj.toString(); // Looks up prototype each time
}
// โ
FAST - Cache the method
const toString = obj.toString;
for (let i = 0; i < 1000000; i++) {
toString.call(obj);
}
2. Avoid Deep Prototype Chains:โ
// โ BAD - 5 levels deep
A โ B โ C โ D โ E โ Object.prototype โ null
// โ
GOOD - 2 levels deep
Child โ Parent โ Object.prototype โ null
3. Use Own Properties for Frequently Accessed Data:โ
// โ SLOW - Method accessed millions of times
Class.prototype.criticalMethod = function() {};
// โ
FAST - Copy to instance if called frequently
this.criticalMethod = Class.prototype.criticalMethod;
Modern Engines Optimize:โ
Modern JavaScript engines (V8, SpiderMonkey) use:
- Inline caching - remembers lookup results
- Hidden classes - optimizes property access
- JIT compilation - compiles hot paths
But deep chains still have overhead!
How instanceof Works Internallyโ
Understanding instanceof helps debug inheritance issues.
Syntax:โ
object instanceof Constructor
What It Checks:โ
Does Constructor.prototype exist anywhere in object's prototype chain?
Manual Implementation:โ
function myInstanceof(obj, Constructor) {
// Get the prototype of the object
let proto = Object.getPrototypeOf(obj);
// Get the prototype property of the constructor
const prototype = Constructor.prototype;
// Walk up the prototype chain
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
Examples:โ
function Animal() {}
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
const dog = new Dog();
console.log(dog instanceof Dog); // true โ
console.log(dog instanceof Animal); // true โ
console.log(dog instanceof Object); // true โ
console.log(dog instanceof Array); // false โ
How It Works:โ
dog instanceof Dog:
dog.__proto__ === Dog.prototype? YES โ
dog instanceof Animal:
dog.__proto__ === Animal.prototype? NO
dog.__proto__.__proto__ === Animal.prototype? YES โ
dog instanceof Object:
dog.__proto__ === Object.prototype? NO
dog.__proto__.__proto__ === Object.prototype? NO
dog.__proto__.__proto__.__proto__ === Object.prototype? YES โ
Gotcha - Changing Prototype:โ
function User() {}
const user = new User();
console.log(user instanceof User); // true โ
// Change prototype
User.prototype = {};
console.log(user instanceof User); // false โ (different prototype now!)
Cross-Frame Issues:โ
// iframe has its own Array constructor
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const iframeArray = iframe.contentWindow.Array;
const arr = new iframeArray();
console.log(arr instanceof Array); // false โ (different Array)
console.log(Array.isArray(arr)); // true โ
(reliable check)
Lesson: Use Array.isArray() for arrays, not instanceof.
Prototype Pitfalls in Real Appsโ
Real-world problems you'll encounter with prototypes.
Pitfall 1: Shared Mutable Stateโ
function User() {}
User.prototype.friends = []; // โ Shared array!
const u1 = new User();
const u2 = new User();
u1.friends.push('Alice');
console.log(u2.friends); // ['Alice'] โ ๏ธ Affected!
Solution:
function User() {
this.friends = []; // โ
Instance property
}
Pitfall 2: Modifying Built-in Prototypes in Librariesโ
// Library A
Array.prototype.remove = function(item) {
const index = this.indexOf(item);
if (index > -1) this.splice(index, 1);
};
// Library B (conflicts!)
Array.prototype.remove = function(index) {
this.splice(index, 1);
};
// Your code - which remove() runs? ๐ฑ
Solution: Never modify built-in prototypes in libraries.
Pitfall 3: Memory Leaks with Closuresโ
function User(name) {
const data = new Array(1000000); // Large data
this.getName = function() {
return name; // Closure keeps entire 'data' in memory! โ
};
}
// โ
Better - no closure, no leak
function User(name) {
this.name = name;
}
User.prototype.getName = function() {
return this.name;
};
Pitfall 4: Incorrect Inheritance Setupโ
// โ WRONG - Calls parent constructor during setup
function Child() {}
Child.prototype = new Parent();
// โ
CORRECT
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Pitfall 5: Forgetting this Contextโ
function User(name) {
this.name = name;
}
User.prototype.greet = function() {
console.log(this.name);
};
const user = new User('Alice');
const greet = user.greet;
greet(); // undefined โ (lost 'this' context)
// โ
Solutions:
greet.call(user); // Use call/apply
user.greet.bind(user)(); // Bind this
() => user.greet(); // Arrow function wrapper
Advanced Interview Questionsโ
Question 1: Explain the outputโ
function User() {}
User.prototype.name = 'Alice';
const u1 = new User();
const u2 = new User();
u1.name = 'Bob';
console.log(u1.name);
console.log(u2.name);
console.log(User.prototype.name);
delete u1.name;
console.log(u1.name);
Click for answer
Output:
Bob
Alice
Alice
Alice
Explanation:
u1.name = 'Bob'creates an own property onu1(shadows prototype)u2.namestill reads from prototype โ'Alice'User.prototype.nameunchanged โ'Alice'- After
delete u1.name, own property removed, falls back to prototype โ'Alice'
Question 2: What's wrong with this code?โ
function Animal() {}
Animal.prototype = {
speak: function() {
console.log('sound');
}
};
function Dog() {}
Dog.prototype = Animal.prototype;
Dog.prototype.bark = function() {
console.log('woof');
};
const animal = new Animal();
animal.bark(); // What happens?
Click for answer
Output: 'woof'
Problem: Dog.prototype = Animal.prototype creates a reference, not a copy!
Why it's bad:
Dog.prototype === Animal.prototype // true โ
// When you add bark to Dog.prototype,
// you're also adding it to Animal.prototype!
Fix:
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Question 3: Multiple Inheritance Simulationโ
// Can we inherit from multiple prototypes?
function CanEat() {}
CanEat.prototype.eat = function() { console.log('eating'); };
function CanWalk() {}
CanWalk.prototype.walk = function() { console.log('walking'); };
function Animal() {}
// How to make Animal inherit from both?
Click for answer
JavaScript doesn't support multiple inheritance directly, but we can simulate it:
// Solution 1: Mixins
Object.assign(Animal.prototype, CanEat.prototype, CanWalk.prototype);
const animal = new Animal();
animal.eat(); // 'eating'
animal.walk(); // 'walking'
// Solution 2: Manual copying
function mixin(target, ...sources) {
sources.forEach(source => {
Object.getOwnPropertyNames(source.prototype).forEach(name => {
if (name !== 'constructor') {
target.prototype[name] = source.prototype[name];
}
});
});
}
mixin(Animal, CanEat, CanWalk);
Note: These are shallow copies, not true inheritance chains.
Question 4: Explain new Behaviorโ
function User(name) {
this.name = name;
return { custom: 'object' };
}
const u1 = new User('Alice');
console.log(u1.name);
console.log(u1.custom);
function Admin(name) {
this.name = name;
return 42; // primitive
}
const a1 = new Admin('Bob');
console.log(a1.name);
Click for answer
Output:
undefined
object
Bob
Explanation:
- If constructor returns an object, that object is returned (ignoring
this) - If constructor returns a primitive or nothing, the newly created object is returned
// User case:
// return { custom: 'object' }; โ returns this object
// u1 = { custom: 'object' }
// u1.name is undefined
// Admin case:
// return 42; โ primitive, ignored
// a1 = { name: 'Bob' } (the object created by new)
Question 5: Prototype Chain Lengthโ
function A() {}
function B() {}
function C() {}
B.prototype = Object.create(A.prototype);
C.prototype = Object.create(B.prototype);
const c = new C();
// How many steps to reach null?
// c โ ? โ ? โ ? โ null
Click for answer
Answer: 4 steps
c
โ C.prototype
โ B.prototype
โ A.prototype
โ Object.prototype
โ null
Code to verify:
let proto = c;
let count = 0;
while ((proto = Object.getPrototypeOf(proto)) !== null) {
count++;
console.log(`Step ${count}:`, proto.constructor?.name || 'Object');
}
console.log(`Total steps: ${count}`); // 4
Killer Interview One-Linersโ
Core Concept:โ
"JavaScript objects inherit from other objects via prototype chains, where __proto__ links instances to constructor prototypes, and new wires this chain during object creation."
__proto__ vs prototype:โ
"__proto__ is the actual link used for lookup on instances, while prototype is the blueprint property on constructors that __proto__ points to."
Inheritance:โ
"Prototypal inheritance creates a chain where property lookup cascades through __proto__ links until the property is found or null is reached."
Memory Efficiency:โ
"Prototype methods are shared across all instances for memory efficiency, while instance properties are unique per object."
Key Takeawaysโ
- โ
Every object has
__proto__(link), only constructor functions haveprototype(blueprint) - โ
object.__proto__ === Constructor.prototypeis the fundamental relationship - โ
newcreates objects, links prototypes, runs constructors, and returns the result - โ
Prototype chain enables inheritance through
__proto__links untilnull - โ
Methods on prototype are shared (memory efficient), properties on
thisare unique - โ
Object.create()creates pure prototypal inheritance without constructors - โ ES6 classes are syntactic sugar over the prototype system
- โ Never modify built-in prototypes in production code
- โ
Use
Object.getPrototypeOf()instead of__proto__in modern code - โ Deep prototype chains have performance costs
Visual Summaryโ
Complete Prototype System:โ
Constructor Function
|
| has property 'prototype'
โ
Constructor.prototype (object)
|
| contains shared methods
|
โ __proto__ points to
|
Instance (created with 'new')
|
| has own properties
|
| can access prototype methods via chain
|
โโ lookup: own props โ prototype โ Object.prototype โ null
The Triangle Relationship:โ
Constructor
|
has ___โ___ references
/ \
prototype instance
\ /
\__โ__/
__proto__
Understanding prototypes is fundamental to mastering JavaScript inheritance, performance optimization, and advanced patterns!