Prototypes

Med

Prototypes are JavaScript's mechanism for inheritance. Every object has a hidden [[Prototype]] link to another object. When you access a property, JS looks up the prototype chain until it finds it or reaches null.

Interactive Visualization

Look up:
Prototype Chain
dog
name:"Rex"
bark:fn()
__proto__: → Animal.prototype
↓ __proto__
Animal.prototype
speak:fn()
eat:fn()
__proto__: → Object.prototype
↓ __proto__
Object.prototype
toString:fn()
hasOwnProperty:fn()
__proto__: → null
↓ __proto__
null
End of chain
Key Insight: Prototype Chain: JS walks up __proto__ links until it finds the property or hits null.

Understanding Prototypes

Prototypes are JavaScript's built-in mechanism for inheritance and shared behavior between objects. Unlike classical languages like Java or C++ that use class-based inheritance, JavaScript uses a prototype chain — a linked list of objects where property lookups travel upward until the property is found or the chain ends at null.

Every object in JavaScript has an internal link called [[Prototype]], which you can access using Object.getPrototypeOf() or the older __proto__ property. When you try to read a property on an object and it does not exist on the object itself, JavaScript automatically looks at the object's prototype, then the prototype's prototype, and so on up the chain. This is called prototype chain delegation and is how methods like toString() and hasOwnProperty() are available on every object even though you never defined them — they are inherited from Object.prototype at the top of the chain.

A common source of confusion is the difference between __proto__ and the .prototype property. Every function in JavaScript has a .prototype property, but this is not the function's own prototype. Instead, it is the object that will become the [[Prototype]] of any new instances created with the new keyword. When you write new Person("Alice"), the newly created object's __proto__ is set to Person.prototype. This is how constructor functions share methods across all instances without duplicating them in memory.

ES6 classes are syntactic sugar over this prototype-based system. When you write class Dog extends Animal, JavaScript sets up the prototype chain so that Dog.prototype.__proto__ points to Animal.prototype. The extends keyword, constructor, and super are all just cleaner syntax for prototype operations that have always existed in the language. Understanding this is important because class-based mental models can lead to confusion when JavaScript's prototype behavior does not match expectations — for example, properties defined in a constructor are own properties on each instance, while methods defined in the class body are shared via the prototype.

One practical use of prototype knowledge is creating objects with no prototype using Object.create(null). These bare objects have no inherited properties, making them ideal for use as dictionaries or lookup maps where you do not want collisions with built-in methods like toString or constructor. This pattern is common in library internals and interview discussions about object safety.

Key Points

  • Every object has a [[Prototype]] (accessible via __proto__ or Object.getPrototypeOf)
  • Property lookup walks up the prototype chain
  • Functions have a .prototype property used for constructor instances
  • Object.create() creates objects with a specific prototype
  • ES6 classes are syntactic sugar over prototypes

Code Examples

Basic Prototype Chain

const animal = {
  eats: true,
  walk() {
    console.log("Walking");
  }
};

const dog = {
  barks: true
};

dog.__proto__ = animal;

dog.barks;  // true (own property)
dog.eats;   // true (from prototype)
dog.walk(); // "Walking" (from prototype)

JS walks up __proto__ links to find properties

Simple Object Literal

const obj = { x: 1 };

// All objects inherit from Object.prototype
obj.__proto__ === Object.prototype  // true

// That's why we can use:
obj.toString();      // "[object Object]"
obj.hasOwnProperty("x");  // true

// These methods come from Object.prototype

Object literals inherit from Object.prototype

Object.create()

const parent = {
  greet() {
    console.log("Hello from " + this.name);
  }
};

const child = Object.create(parent);
child.name = "Child";

child.greet();  // "Hello from Child"

// Object.create() sets the prototype
Object.getPrototypeOf(child) === parent  // true

Create object with specific prototype

Property Shadowing

const parent = {
  name: "Parent",
  greet() {
    return "Hi from " + this.name;
  }
};

const child = Object.create(parent);
child.name = "Child";  // shadows parent.name

child.greet();  // "Hi from Child"

// child.name shadows parent.name
// but greet() is still from parent

Child property hides parent property

Constructor Function

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log("Hi, I'm " + this.name);
};

const alice = new Person("Alice");
const bob = new Person("Bob");

alice.greet();  // "Hi, I'm Alice"
bob.greet();    // "Hi, I'm Bob"

// Both share the same greet method!
alice.greet === bob.greet  // true

Instances inherit from Constructor.prototype

hasOwnProperty Check

const parent = { inherited: true };
const child = Object.create(parent);
child.own = true;

// hasOwnProperty: only own properties
child.hasOwnProperty("own");       // true
child.hasOwnProperty("inherited"); // false

// 'in' operator: checks entire chain
"own" in child;        // true
"inherited" in child;  // true

// Useful for safe iteration
for (let key in child) {
  if (child.hasOwnProperty(key)) {
    console.log(key);  // only "own"
  }
}

hasOwnProperty checks only object, "in" checks chain

Object.create(null)

// Normal object has prototype
const normal = {};
normal.toString;  // [Function]

// Null prototype = truly empty
const dict = Object.create(null);
dict.toString;    // undefined

// Safe dictionary (no collisions)
dict["hasOwnProperty"] = "safe!";
dict.hasOwnProperty;  // "safe!"

// With normal object this would
// shadow the built-in method

No prototype = no inherited methods, safe dictionaries

Class Syntax (ES6)

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(this.name + " makes a sound");
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + " barks");
  }
}

const dog = new Dog("Rex");
dog.speak();  // "Rex barks"

// Classes are syntactic sugar!
// Under the hood: prototypes

Classes are syntactic sugar over prototypes

Common Mistakes

  • Confusing __proto__ with .prototype
  • Modifying Object.prototype (affects all objects!)
  • Not understanding that arrays/functions are also objects with prototypes

Interview Tips

  • Draw the prototype chain for a given object
  • Explain the difference between __proto__ and .prototype
  • Know how ES6 classes relate to prototypes

Related Concepts