Prototypes are a core concept in JavaScript, forming the foundation of its object-oriented programming (OOP) capabilities. While other languages use classes as the basis for inheritance, JavaScript relies on prototypes. In this article, we’ll explore prototypes in depth and uncover how they power inheritance, object behavior, and more in JavaScript.
What Are Prototypes?
In JavaScript, every object has an internal property called [[Prototype]]
that points to another object. This is the prototype of the object, and it acts as a fallback mechanism for properties or methods that are not found directly on the object.
Prototype Chain
The prototype chain is a series of linked prototypes. If a property or method isn't found on an object, JavaScript looks up the chain until it reaches null
.
const parent = { greet: () => console.log("Hello from parent!") };
const child = Object.create(parent);
child.greet(); // Output: "Hello from parent!"
console.log(child.hasOwnProperty('greet')); // Output: false
Here, child
doesn't have a greet
method, so JavaScript looks up the prototype chain to parent
and finds it there.
The __proto__
and prototype
Confusion
JavaScript provides two different terms related to prototypes that can be confusing:
-
__proto__
:- This is an accessor property available on all objects that points to the object's prototype.
- It's a way to access the
[[Prototype]]
of an object.
-
prototype
:- This is a property available only on functions (specifically constructor functions).
- It’s used to define the prototype of objects created by that function.
Example:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log(`Hello, my name is ${this.name}`);
};
const alice = new Person("Alice");
console.log(alice.__proto__ === Person.prototype); // true
alice.sayHello(); // Output: "Hello, my name is Alice"
Prototype Inheritance in Action
JavaScript’s inheritance is prototype-based, meaning objects inherit directly from other objects rather than classes.
Creating Inheritance
const animal = {
eat() {
console.log("Eating...");
}
};
const dog = Object.create(animal);
dog.bark = function () {
console.log("Barking...");
};
dog.eat(); // Output: "Eating..."
dog.bark(); // Output: "Barking..."
The dog
object inherits the eat
method from the animal
object.
Using Object.create
for Clean Inheritance
The Object.create
method creates a new object with a specified prototype. It's a cleaner and more intuitive way to set up inheritance.
Example:
const person = {
introduce() {
console.log(`Hi, I'm ${this.name}`);
}
};
const student = Object.create(person);
student.name = "John";
student.introduce(); // Output: "Hi, I'm John"
Extending Built-In Prototypes
While extending built-in prototypes like Array
or Object
is possible, it’s generally discouraged as it can lead to conflicts.
Example:
Array.prototype.last = function () {
return this[this.length - 1];
};
console.log([1, 2, 3].last()); // Output: 3
Why Avoid It?
- Compatibility issues: Other libraries might rely on default prototypes.
- Maintenance: Your changes might break existing code.
Prototypes vs Classes
With ES6, JavaScript introduced class
syntax, providing a more familiar OOP experience. However, under the hood, classes still use prototypes.
Example:
class Animal {
eat() {
console.log("Eating...");
}
}
class Dog extends Animal {
bark() {
console.log("Barking...");
}
}
const dog = new Dog();
dog.eat(); // Output: "Eating..."
dog.bark(); // Output: "Barking..."
Even with class
, the inheritance is prototype-based.
The Performance Angle
Prototype-based inheritance is more memory-efficient because methods are shared across instances rather than being duplicated.
Example:
function Car(make) {
this.make = make;
}
Car.prototype.drive = function () {
console.log(`${this.make} is driving`);
};
const car1 = new Car("Toyota");
const car2 = new Car("Honda");
console.log(car1.drive === car2.drive); // true
Here, drive
is not duplicated for each car; instead, both instances share the same method.
Key Takeaways
- Prototypes Enable Inheritance: Objects inherit from other objects through their prototype.
- Prototype Chain: JavaScript resolves properties and methods by traversing the prototype chain.
-
Object.create
: A clean way to set up inheritance. - Avoid Extending Built-In Prototypes: It can lead to unexpected behavior and conflicts.
- Classes Use Prototypes: ES6 classes provide syntactic sugar but still rely on prototypes under the hood.
Understanding prototypes is essential for mastering JavaScript. While ES6 classes have made object-oriented programming in JavaScript more approachable, the prototype system remains at the core of the language. By diving deep into prototypes, you unlock the ability to write efficient, scalable, and maintainable code.
Top comments (0)