Object Prototypes (JavaScript)
Learn Object Prototypes (JavaScript) step by step with clear examples and exercises.
Why This Matters
In this full guide, we will delve deep into understanding JavaScript Object Prototypes, a crucial concept in object-oriented programming (OOP). By mastering prototypes, you'll be able to write efficient JavaScript code, especially for larger applications or complex objects. Additionally, familiarity with prototypes can help troubleshoot real-world bugs and prepare for coding interviews.
Why This Matters
JavaScript object prototypes play a vital role in creating reusable code by allowing us to create new objects with shared properties and methods. Understanding prototypes is essential for writing efficient JavaScript code, especially when dealing with larger applications or complex objects. Furthermore, familiarity with prototypes can help you troubleshoot real-world bugs and prepare for coding interviews.
Prerequisites
Before diving into the core concept of object prototypes, it is essential to have a good understanding of the following topics:
- JavaScript basics (variables, data types, operators)
- Objects (creating, accessing properties and methods)
- Functions in JavaScript (declaration, invocation, anonymous functions)
- The
thiskeyword in JavaScript - Understanding the concept of inheritance and object-oriented programming (OOP)
Core Concept
Understanding Prototypes
Every object in JavaScript has an internal property called [[Prototype]], which points to another object. This prototype object can have its own prototype, and so on, forming a chain known as the prototype chain. When you access or modify properties of an object, JavaScript first looks for that property within the object itself. If it's not found, it moves up the prototype chain until it finds the property or reaches the end of the chain (Object.prototype).
Creating Objects with Prototypes
You can create a constructor function to create objects with shared properties and methods. The constructor function's prototype property points to an object that serves as the prototype for all objects created by the constructor.
function Car(make, model) {
this.make = make;
this.model = model;
}
Car.prototype.drive = function() {
console.log(`Driving a ${this.make} ${this.model}`);
};
const myCar = new Car('Toyota', 'Corolla');
myCar.drive(); // Output: Driving a Toyota Corolla
In this example, the Car constructor function creates objects with properties make and model. It also defines a method drive() on its prototype object, which is inherited by all Car instances.
Changing Prototypes
You can change an object's prototype by setting its [[Prototype]] property directly or using the Object.setPrototypeOf() method. This can be useful in certain situations, such as creating custom objects that inherit from multiple parent classes.
const myCar = { make: 'Toyota', model: 'Corolla' };
myCar.__proto__ = Car.prototype; // Changing the prototype directly
myCar.drive(); // Output: Driving a undefined Corolla (since we haven't set the values for `make` and `model`)
Inheritance and Prototypes
By creating a child constructor that inherits from a parent constructor, you can create objects with properties and methods from both constructors. This is achieved by setting the child constructor's prototype property to an instance of the parent constructor or using Object.create().
function Car(make, model) {
this.make = make;
this.model = model;
}
Car.prototype.drive = function() {
console.log(`Driving a ${this.make} ${this.model}`);
};
function Sedan(make, model, seats) {
Car.call(this, make, model); // Calling the constructor of Car to set the `make` and `model` properties
this.seats = seats;
}
Sedan.prototype = Object.create(Car.prototype); // Creating a new object with Car.prototype as its prototype
Sedan.prototype.constructor = Sedan; // Setting the constructor property to Sedan
const mySedan = new Sedan('Toyota', 'Corolla', 5);
mySedan.drive(); // Output: Driving a Toyota Corolla
console.log(mySedan.seats); // Output: 5
Worked Example
Let's create a simple example of a Person constructor with properties like name, age, and methods like introduce(). We'll also create an Employee constructor that inherits from the Person constructor and adds a salary property and a method to calculate the annual salary.
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.introduce = function() {
console.log(`Hello! I'm ${this.name}, and I'm ${this.age} years old.`);
};
function Employee(name, age, salary) {
Person.call(this, name, age); // Calling the constructor of Person to set the `name` and `age` properties
this.salary = salary;
}
Employee.prototype = Object.create(Person.prototype); // Creating a new object with Person.prototype as its prototype
Employee.prototype.constructor = Employee; // Setting the constructor property to Employee
Employee.prototype.calculateAnnualSalary = function() {
return this.salary * 12;
};
const john = new Employee('John', 30, 5000);
john.introduce(); // Output: Hello! I'm John, and I'm 30 years old.
console.log(john.calculateAnnualSalary()); // Output: 60000
Common Mistakes
- Forgetting to call the constructor of the parent class when creating a child class (as shown in the worked example above)
- Setting the
prototypeproperty directly instead of usingObject.create()(which ensures the new object inherits all properties and methods from the prototype object) - Changing the
[[Prototype]]property directly on an object without considering the consequences (e.g., losing access to inherited properties and methods) - Misunderstanding how the prototype chain works and assuming that objects inherit properties and methods directly from their parent class constructor
- Not realizing that changing a constructor's
prototypeproperty affects all objects created by that constructor - Incorrectly using the
thiskeyword when defining methods on constructors or prototypes, leading to unexpected results - Failing to properly set up inheritance between constructors, resulting in missing properties or methods on child objects
- Overcomplicating object creation and inheritance by not utilizing constructor functions and prototypes effectively
Practice Questions
- Create a
Rectangleconstructor with propertieswidthandheight. Define a method to calculate the area of the rectangle. Create an instance ofRectangle, set its dimensions, and call the method to calculate the area. - Modify the
Personconstructor from the worked example to include agenderproperty. Create a newEmployeeinstance with a gender and verify that it has the expected properties and methods. - Create a custom object called
Vehiclewith propertiesmake,model, andyear. Add a method to display the vehicle's information. Then, create an object for a car and another for a bike, both inheriting from theVehicleconstructor. - Create a
Shapeconstructor that takes no arguments but has propertiescolorand methodsgetColor()andarea(). Thearea()method should return an error message because shapes don't have an area by default. Create aCircleconstructor that inherits fromShape, adds aradiusproperty, and overrides thearea()method to calculate the area of a circle using the formula πr². Create an instance ofCircle, set its radius, and call thearea()method to calculate the area. - Modify the
Employeeconstructor from the worked example to include apositionproperty. Create a newManagerconstructor that inherits fromEmployeeand adds ateamSizeproperty. Override thecalculateAnnualSalary()method to account for a manager's bonus based on team size (e.g., a 10% bonus for every additional employee). Create a newManagerinstance, set its properties, and call thecalculateAnnualSalary()method to calculate the annual salary.
FAQ
What is the purpose of JavaScript prototypes?
- Prototypes enable code reusability by allowing us to create new objects with shared properties and methods.
How does the prototype chain work in JavaScript?
- When you access or modify properties of an object, JavaScript first looks for that property within the object itself. If it's not found, it moves up the prototype chain until it finds the property or reaches the end of the chain (
Object.prototype).
How can I change an object's prototype in JavaScript?
- You can change an object's prototype by setting its
[[Prototype]]property directly or using theObject.setPrototypeOf()method.
What is the difference between changing an object's prototype and changing its properties?
- Changing an object's prototype affects all objects created by that constructor, while changing an object's properties only affects that specific object.
Why should I use prototypes in JavaScript instead of creating new objects with literal notation every time?
- Using prototypes enables code reusability and makes it easier to manage shared properties and methods across multiple objects. It also simplifies the creation of complex objects by allowing you to define their structure once and then create as many instances as needed.
What is the difference between a constructor function and an instance method?
- A constructor function creates new objects, while an instance method is a method defined on the prototype object that can be called on any instance of the object created by the constructor.
How do I access and modify properties of an object's prototype in JavaScript?
- You can access and modify properties of an object's prototype using
Object.getPrototypeOf()to get the prototype, and then accessing or modifying properties directly on the returned object. For example:
const myCar = new Car('Toyota', 'Corolla');
const carPrototype = Object.getPrototypeOf(myCar);
carPrototype.driveSpeed = 60; // Adding a property to the prototype
console.log(myCar.driveSpeed); // Output: 60
How can I create an object with no prototype in JavaScript?
- To create an object without a prototype, you can use
Object.create(null). This creates an empty object with no prototype (i.e., its[[Prototype]]property isnull).
What happens when I call a method on an object that doesn't exist in the object or its prototype chain?
- If you call a method on an object that doesn't exist in the object or its prototype chain, JavaScript will return
undefined. However, if you use strict mode (by adding"use strict"at the beginning of your script), it will throw aReferenceError.
How can I check whether an object has a certain property or method?
- You can check whether an object has a certain property or method by using the
inoperator:
const myCar = new Car('Toyota', 'Corolla');
console.log('drive' in myCar); // Output: true
console.log('unknownMethod' in myCar); // Output: false
What is the difference between Object.create() and using an empty object as a prototype?
- Using an empty object as a prototype (e.g.,
{}) creates an object with no inherited properties or methods, whileObject.create(null)creates an object with no prototype (i.e., its[[Prototype]]property isnull). When using an empty object as a prototype, the new object still inherits fromObject.prototype.
How can I create a private property in JavaScript?
- To create a private property in JavaScript, you can use closures or ES6 class syntax with
#prefix for private properties. For example:
function Car(make, model) {
let _make = make;
let _model = model;
this.getMake = function() {
return _make;
};
this.setMake = function(newMake) {
_make = newMake;
};
}
In this example, _make and _model are private properties that can only be accessed through the public methods getMake() and setMake().