- Published on
Part 3: Prototypes, Inheritance, and the JavaScript Object Model
- Authors
- Name
- Diego Herrera Redondo
- @diegxherrera
Welcome to Part 13 of our JavaScript Deep Dive series! 🎉 In this part, we’re exploring prototypes, inheritance, and the JavaScript object model. These concepts are fundamental to understanding JavaScript's object-oriented capabilities and will lay a solid foundation for our upcoming TypeScript extension. Let’s dive into the heart of JavaScript’s unique object system! 🚀
Understanding Prototypes in JavaScript 🔍
JavaScript is a prototype-based language, meaning objects can inherit properties from other objects. Unlike traditional class-based languages, JavaScript uses prototypes to create inheritance structures.
Every JavaScript object has a hidden property called [[Prototype]]
, which links it to another object. This link is what forms the prototype chain.
Creating and Accessing Prototypes
When you create an object using an object literal or new
, it automatically links to Object.prototype
or the constructor’s prototype.
let person = { name: "Alice" };
console.log(person.__proto__ === Object.prototype); // true
You can access an object’s prototype using __proto__
, although modern JavaScript prefers using Object.getPrototypeOf()
.
Prototype Chain and Inheritance 🔗
When you try to access a property on an object, JavaScript first checks the object itself. If the property isn’t found, it moves up the prototype chain until it finds the property or reaches null
.
Example of Prototype Chain
let animal = { eats: true };
let dog = { barks: true };
dog.__proto__ = animal; // Setting animal as the prototype of dog
console.log(dog.barks); // true (found on dog)
console.log(dog.eats); // true (found on animal prototype)
In this example, dog
has animal
as its prototype, so it can access eats
even though it’s not directly on dog
.
new
Keyword 🛠️
Using Constructor Functions and Constructor functions create new objects and automatically set their prototype.
Example: Constructor Function
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}.`);
};
let john = new Person("John", 30);
john.greet(); // Output: Hello, my name is John.
Explanation:
Person
is a constructor function that assignsname
andage
.- Using
new
withPerson
creates an object and sets its prototype toPerson.prototype
.
new
Keyword
The When we call new Person()
, JavaScript:
- Creates an empty object.
- Sets the object's
[[Prototype]]
toPerson.prototype
. - Executes the constructor function with
this
bound to the new object. - Returns the object.
Modern JavaScript Classes 🎩
ES6 introduced classes as a syntactical sugar over prototypes to make inheritance easier and more readable.
Example: Defining a Class
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
let rover = new Dog("Rover");
rover.speak(); // Output: Rover barks.
Explanation:
Animal
is a base class with aspeak
method.Dog
is a subclass ofAnimal
, overridingspeak
.
Classes in JavaScript are still prototype-based, but the syntax is cleaner and closer to traditional OOP languages.
Object.create()
and Direct Prototype Linkage 🧩
JavaScript allows us to create objects with a specified prototype using Object.create()
.
Example
let vehicle = { wheels: 4 };
let car = Object.create(vehicle);
console.log(car.wheels); // Output: 4
console.log(Object.getPrototypeOf(car) === vehicle); // true
In this example, car
directly inherits properties from vehicle
, making Object.create()
useful for setting up prototype chains without constructors.
Practical Uses of Prototypes 📌
- Shared Methods: Attach methods to a constructor’s prototype to save memory. Each object created will share the same method rather than creating a new one.
function User(name) {
this.name = name;
}
User.prototype.sayHi = function() {
console.log(`Hi, ${this.name}!`);
};
let user1 = new User("Alice");
let user2 = new User("Bob");
user1.sayHi(); // Output: Hi, Alice!
user2.sayHi(); // Output: Hi, Bob!
Inheritance: Set up relationships between different object types.
Type Checking: Prototypes help identify types when working with complex data structures.
hasOwnProperty
and in
Keyword ⚙️
To check if an object has a property directly (not from its prototype), use hasOwnProperty
.
let cat = { fur: true };
console.log(cat.hasOwnProperty("fur")); // true
console.log("toString" in cat); // true (inherited from Object prototype)
The in
operator checks if a property exists in the object, including inherited properties.
Practice Challenge: Prototypal Inheritance 🎲
Let’s practice setting up a simple inheritance structure.
- Create a
Vehicle
constructor that has atype
property and adescribe
method. - Create a
Car
constructor that inherits fromVehicle
. - Use the
Car
constructor to create a new car object.
Example Solution
function Vehicle(type) {
this.type = type;
}
Vehicle.prototype.describe = function() {
console.log(`This is a ${this.type}.`);
};
function Car(make, model) {
Vehicle.call(this, "Car"); // Inherit Vehicle properties
this.make = make;
this.model = model;
}
Car.prototype = Object.create(Vehicle.prototype); // Inherit Vehicle methods
Car.prototype.constructor = Car; // Correct constructor reference
let myCar = new Car("Toyota", "Corolla");
myCar.describe(); // Output: This is a Car.
Explanation:
Vehicle
is the parent constructor, anddescribe
is added toVehicle.prototype
.Car
is the child constructor, and we useVehicle.call(this, "Car")
to inherit properties.- We set
Car.prototype
toObject.create(Vehicle.prototype)
for method inheritance.
Wrapping Up
In Part 3, we explored prototypes, inheritance, and JavaScript’s object model. This deep dive into the mechanics of JavaScript objects is essential to understanding how JavaScript achieves inheritance and will serve as a solid foundation for TypeScript, where classes and interfaces become even more powerful.