JavaScript Prototype

Prototypes are the mechanism by which JavaScript objects inherit properties and methods from other objects. Understanding prototypes is key to mastering JavaScript's object-oriented programming model.

Try It Yourself

Explore prototypes and inheritance in action:

index.js
// JavaScript Prototype in action

// Create a prototype object
const animal = {
  alive: true,
  speak() {
    console.log(this.name + " makes a sound");
  }
};

// Create object that inherits from animal
const dog = Object.create(animal);
dog.name = "Rex";
dog.breed = "German Shepherd";

// dog inherits from animal
console.log("dog.alive:", dog.alive); // true (from prototype)
dog.speak(); // "Rex makes a sound"

// Check the prototype chain
console.log("\nPrototype of dog:", Object.getPrototypeOf(dog) === animal);
console.log("dog has own 'name':", dog.hasOwnProperty("name"));
console.log("dog has own 'alive':", dog.hasOwnProperty("alive"));

// Override inherited property
dog.speak = function() {
  console.log(this.name + " barks!");
};
dog.speak(); // "Rex barks!" (own method)

What is a Prototype?

Every JavaScript object has a hidden [[Prototype]] property that links to another object (its prototype). When you access a property that doesn't exist on an object, JavaScript looks up the prototype chain.

javascript
// Every object has a prototype (except Object.prototype)
const obj = { name: "Alice" };

// Get the prototype
console.log(Object.getPrototypeOf(obj)); // Object.prototype

// Object.prototype is the end of the chain
console.log(Object.getPrototypeOf(Object.prototype)); // null

// Arrays have Array.prototype in their chain
const arr = [1, 2, 3];
console.log(Object.getPrototypeOf(arr)); // Array.prototype
console.log(Object.getPrototypeOf(Array.prototype)); // Object.prototype
The Prototype Chain
The chain continues until it reaches Object.prototype, whose prototype is null. This is the end of every prototype chain.

The Prototype Chain

When accessing a property, JavaScript walks up the prototype chain until it finds the property or reaches the end:

javascript
// The prototype chain visualized

const grandparent = {
  familyName: "Smith",
  sayHello() {
    console.log("Hello from " + this.familyName);
  }
};

const parent = Object.create(grandparent);
parent.generation = "parent";

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

// Property lookup walks the chain
console.log(child.name);        // "Alice" (own property)
console.log(child.generation);  // "parent" (from parent)
console.log(child.familyName);  // "Smith" (from grandparent)
child.sayHello();               // "Hello from Smith"

// Visualize the chain
// child -> parent -> grandparent -> Object.prototype -> null

Constructor Function Prototypes

Constructor functions have a prototype property. Objects created with new inherit from this prototype:

javascript
// Constructor functions have a prototype property

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

// Add methods to the prototype
Person.prototype.greet = function() {
  return "Hi, I'm " + this.name;
};

Person.prototype.species = "Homo sapiens";

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

// Instances inherit from Person.prototype
console.log(alice.greet()); // "Hi, I'm Alice"
console.log(bob.species);   // "Homo sapiens"

// Both share the same prototype
console.log(Object.getPrototypeOf(alice) === Person.prototype); // true
console.log(alice.greet === bob.greet); // true (same function)

__proto__ vs prototype

These are often confused but serve different purposes:

javascript
// __proto__ vs prototype - they're different!

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

Dog.prototype.bark = function() {
  console.log(this.name + " barks!");
};

const rex = new Dog("Rex");

// prototype is on constructor functions
console.log(Dog.prototype); // { bark: [Function], constructor: Dog }

// __proto__ is on instances (points to constructor's prototype)
console.log(rex.__proto__ === Dog.prototype); // true

// Use Object.getPrototypeOf() instead of __proto__
console.log(Object.getPrototypeOf(rex) === Dog.prototype); // true

// The chain:
// rex -> Dog.prototype -> Object.prototype -> null
Feature__proto__prototype
Found onAll objectsConstructor functions
PurposePoints to object's prototypeBecomes __proto__ of instances
AccessObject.getPrototypeOf(obj)Constructor.prototype
Modern usageUse Object.getPrototypeOf()Still common
Avoid __proto__
While __proto__ works, it's deprecated. Use Object.getPrototypeOf() to read and Object.setPrototypeOf() to write (though setting is slow).

Object.create()

The cleanest way to create an object with a specific prototype:

javascript
// Object.create() - create object with specific prototype

const personProto = {
  greet() {
    return "Hello, I'm " + this.name;
  },
  describe() {
    return this.name + " is " + this.age + " years old";
  }
};

// Create object with personProto as prototype
const alice = Object.create(personProto);
alice.name = "Alice";
alice.age = 25;

console.log(alice.greet());    // "Hello, I'm Alice"
console.log(alice.describe()); // "Alice is 25 years old"

// Create with properties defined
const bob = Object.create(personProto, {
  name: { value: "Bob", writable: true },
  age: { value: 30, writable: true }
});

console.log(bob.greet()); // "Hello, I'm Bob"

// Create object with null prototype (no inherited methods)
const bare = Object.create(null);
bare.name = "Bare object";
// bare.toString(); // Error! No inherited methods

Prototypal Inheritance

You can set up inheritance between constructor functions:

javascript
// Prototypal inheritance between constructors

// 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) {
  Animal.call(this, name); // Call parent constructor
  this.breed = breed;
}

// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // Fix constructor reference

// Add child-specific methods
Dog.prototype.bark = function() {
  console.log(this.name + " barks!");
};

// Override parent method
Dog.prototype.speak = function() {
  console.log(this.name + " woofs!");
};

const rex = new Dog("Rex", "German Shepherd");
rex.speak(); // "Rex woofs!" (overridden)
rex.bark();  // "Rex barks!"

console.log(rex instanceof Dog);    // true
console.log(rex instanceof Animal); // true

Own vs Inherited Properties

Use hasOwnProperty() to distinguish between own and inherited properties:

javascript
// Distinguishing own vs inherited properties

const proto = { inherited: "from prototype" };
const obj = Object.create(proto);
obj.own = "my own property";

// Both are accessible
console.log(obj.own);       // "my own property"
console.log(obj.inherited); // "from prototype"

// Check if property is own
console.log(obj.hasOwnProperty("own"));       // true
console.log(obj.hasOwnProperty("inherited")); // false

// 'in' operator checks entire chain
console.log("own" in obj);       // true
console.log("inherited" in obj); // true

// Get only own properties
console.log(Object.keys(obj));           // ["own"]
console.log(Object.getOwnPropertyNames(obj)); // ["own"]

// for...in iterates inherited properties too
for (const key in obj) {
  console.log(key, "- own:", obj.hasOwnProperty(key));
}
// own - own: true
// inherited - own: false

Modifying Prototypes

Changes to a prototype affect all objects that inherit from it:

javascript
// Modifying prototypes affects all instances

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

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

// Add method to prototype AFTER creating instances
User.prototype.greet = function() {
  return "Hello, I'm " + this.name;
};

// Both instances can use the new method!
console.log(alice.greet()); // "Hello, I'm Alice"
console.log(bob.greet());   // "Hello, I'm Bob"

// Warning: Don't modify built-in prototypes!
// This is generally bad practice:
// Array.prototype.first = function() { return this[0]; };

// Use Object.freeze() to prevent prototype modification
Object.freeze(User.prototype);
Don't Modify Built-in Prototypes
Modifying Array.prototype, Object.prototype, etc. can break code and cause conflicts with other libraries. This is considered bad practice.

Classes vs Prototypes

ES6 classes are syntactic sugar over prototypes:

javascript
// ES6 classes are syntactic sugar over prototypes

// Class syntax
class PersonClass {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return "Hi, I'm " + this.name;
  }
}

// Equivalent prototype syntax
function PersonProto(name) {
  this.name = name;
}

PersonProto.prototype.greet = function() {
  return "Hi, I'm " + this.name;
};

// Both work the same way
const p1 = new PersonClass("Alice");
const p2 = new PersonProto("Bob");

console.log(p1.greet()); // "Hi, I'm Alice"
console.log(p2.greet()); // "Hi, I'm Bob"

// Classes still use prototypes under the hood
console.log(typeof PersonClass); // "function"
console.log(PersonClass.prototype.greet); // [Function: greet]
Use Classes in Modern Code
While understanding prototypes is essential, use ES6 classes for cleaner syntax in new code. They work the same way under the hood.

Test Your Knowledge

JavaScript Prototype Quiz

5 questions
Question 1

What happens when you access a property that doesn't exist on an object?

Question 2

What is at the end of every prototype chain?

Question 3

What is the difference between __proto__ and prototype?

Question 4

How do you create an object with a specific prototype?

Question 5

Why add methods to the prototype instead of inside the constructor?

Coding Challenge

Build a Vehicle Inheritance Hierarchy
Hard

Create a prototype-based inheritance hierarchy with Vehicle as the base, and Car and Motorcycle as specialized types. Use Object.create() for inheritance.

Starter Code
// Challenge: Create a prototype-based inheritance hierarchy
// Requirements:
// - Vehicle prototype with: start(), stop(), describe()
// - Car that inherits from Vehicle, adds: doors, drive()
// - Motorcycle that inherits from Vehicle, adds: wheelie()
// Use Object.create() for inheritance

const Vehicle = {
  // Your code here
};

// Create Car and Motorcycle that inherit from Vehicle

// Test your implementation
const car = Object.create(Car);
car.brand = "Toyota";
car.model = "Camry";
car.doors = 4;

console.log(car.describe());
car.start();
car.drive();
car.stop();

const bike = Object.create(Motorcycle);
bike.brand = "Harley";
bike.model = "Sportster";

console.log(bike.describe());
bike.start();
bike.wheelie();
bike.stop();

Summary

  • Every object has a [[Prototype]] linking to another object
  • The prototype chain is used to look up properties not found on the object itself
  • prototype is on constructor functions; __proto__ is on instances
  • Use Object.create(proto) to create objects with a specific prototype
  • Use hasOwnProperty() to check if a property is inherited or own
  • Don't modify built-in prototypes like Array.prototype
  • ES6 classes are syntactic sugar over prototypes