In JavaScript, every object has an internal property called [[Prototype]]
. This property points to another object, which is known as the prototype of the original object. When you try to access a property or method on an object, JavaScript first checks if the object itself has that property or method. If it doesn’t, JavaScript looks for it in the object’s prototype. This process continues up the prototype chain until the property is found or the end of the chain (where the prototype is null
) is reached.
// Create a simple object
const person = {
name: 'John',
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
};
// Create another object that inherits from person
const student = Object.create(person);
student.age = 20;
console.log(student.name); // Output: John
student.greet(); // Output: Hello, my name is John
Prototypal inheritance is the mechanism by which objects can inherit properties and methods from other objects. It is based on the prototype chain. When an object inherits from another object, it gains access to all the properties and methods of its prototype and all the prototypes in the chain above it.
// Define a prototype object
const animal = {
makeSound: function() {
console.log('Some generic sound');
}
};
// Create a dog object that inherits from animal
const dog = Object.create(animal);
dog.makeSound = function() {
console.log('Woof!');
};
dog.makeSound(); // Output: Woof!
There are several ways to create objects with prototypes in JavaScript:
Object.create()
The Object.create()
method creates a new object with the specified prototype object.
const vehicle = {
move: function() {
console.log('Moving...');
}
};
const car = Object.create(vehicle);
car.speed = 60;
car.move(); // Output: Moving...
Constructor functions can also be used to create objects with prototypes. When a constructor function is called with the new
keyword, the newly created object inherits from the constructor’s prototype
property.
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`);
};
const jane = new Person('Jane');
jane.greet(); // Output: Hello, my name is Jane
You can modify the prototype chain of an object at runtime using the __proto__
property (although this is not recommended for performance reasons) or the Object.setPrototypeOf()
method.
const parent = {
sayHello: function() {
console.log('Hello from parent');
}
};
const child = {};
Object.setPrototypeOf(child, parent);
child.sayHello(); // Output: Hello from parent
One of the main advantages of using prototypes is the ability to share methods among multiple objects. Instead of creating a copy of the method for each object, you can define the method on the prototype, and all objects that inherit from it can use the same method.
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.getArea = function() {
return Math.PI * this.radius * this.radius;
};
const circle1 = new Circle(5);
const circle2 = new Circle(10);
console.log(circle1.getArea()); // Output: approximately 78.54
console.log(circle2.getArea()); // Output: approximately 314.16
Prototypes can be used for object composition, which is the process of combining multiple objects to create a new object with the combined functionality.
// Define some prototype objects
const canSwim = {
swim: function() {
console.log('Swimming...');
}
};
const canFly = {
fly: function() {
console.log('Flying...');
}
};
// Create a duck object that combines canSwim and canFly
const duck = Object.assign({}, canSwim, canFly);
duck.swim(); // Output: Swimming...
duck.fly(); // Output: Flying...
Prototype pollution is a security vulnerability that occurs when an attacker is able to modify the prototype of an object. This can lead to unexpected behavior and security risks. To avoid prototype pollution, be careful when using user - input to modify objects and avoid using __proto__
to directly modify the prototype chain.
// Bad practice: Potential prototype pollution
const userInput = JSON.parse('{"__proto__": {"isAdmin": true}}');
const newUser = {};
console.log(newUser.isAdmin); // Output: true (if prototype pollution occurs)
// Good practice: Use Object.create() to avoid prototype pollution
const safeUser = Object.create(null);
// Now the userInput cannot pollute the prototype
Accessing properties and methods through the prototype chain can have a performance impact, especially if the chain is long. Try to keep the prototype chain short and avoid deeply nested inheritance. Also, avoid modifying the prototype chain frequently as it can be an expensive operation.
JavaScript prototypes and prototypal inheritance are powerful concepts that allow for code reuse and the creation of complex object hierarchies. By understanding how prototypes work and how to use them effectively, you can write more efficient and maintainable JavaScript code. Remember to follow best practices such as avoiding prototype pollution and considering performance when working with prototypes.