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:
// 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.
// 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.prototypeThe Prototype Chain
When accessing a property, JavaScript walks up the prototype chain until it finds the property or reaches the end:
// 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 -> nullConstructor Function Prototypes
Constructor functions have a prototype property. Objects created with new inherit from this prototype:
// 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:
// __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 on | All objects | Constructor functions |
| Purpose | Points to object's prototype | Becomes __proto__ of instances |
| Access | Object.getPrototypeOf(obj) | Constructor.prototype |
| Modern usage | Use Object.getPrototypeOf() | Still common |
Object.create()
The cleanest way to create an object with a specific prototype:
// 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 methodsPrototypal Inheritance
You can set up inheritance between constructor functions:
// 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); // trueOwn vs Inherited Properties
Use hasOwnProperty() to distinguish between own and inherited properties:
// 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: falseModifying Prototypes
Changes to a prototype affect all objects that inherit from it:
// 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);Classes vs Prototypes
ES6 classes are syntactic sugar over prototypes:
// 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]Test Your Knowledge
JavaScript Prototype Quiz
5 questionsWhat happens when you access a property that doesn't exist on an object?
What is at the end of every prototype chain?
What is the difference between __proto__ and prototype?
How do you create an object with a specific prototype?
Why add methods to the prototype instead of inside the constructor?
Coding Challenge
Create a prototype-based inheritance hierarchy with Vehicle as the base, and Car and Motorcycle as specialized types. Use Object.create() for inheritance.
// 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