16 KiB
Agenda
Topics to cover in Javascript Oops
- This keyword
- Constructor functions
- Classes & Classical Inheritance
- Prototype & Prototypal Inheritance
- Call ,Apply & Bind Methods
- Memory of objects (Reference Datatypes)
We will try to cover most of these topics in today's sessions and the remaining in the next.
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
Classes and Constructors in OOP
The concept of classes and constructors in the context of Object-Oriented Programming (OOP) and the similarities between classes and constructor functions. Here's a more organized explanation of the concepts:
-
Classes: In modern JavaScript (ES6 and later), classes provide a way to define blueprints for creating objects. A class is a template for creating objects with shared properties and methods.
-
Constructor Functions: In pre-ES6 JavaScript, constructor functions were widely used to create objects with similar properties and methods. They serve as templates for creating instances of objects.
Shared Properties and Methods:
- Both classes and constructor functions allow you to create multiple objects (instances) that share the same properties and methods defined by the class or constructor.
Constructor Method:
- In both classes and constructor functions, the constructor method is used to initialize the properties of an object when it's created. It's automatically invoked when you create a new instance of the class or constructor.
Using this
:
- Within the constructor method, the
this
keyword is used to refer to the current instance being created. It's used to set the initial values of the instance's properties.
Example (Assuming Based on Context):
Here's a restructured example based on the context you've provided:
// Class (ES6+)
class Person {
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
}
// Creating instances of the Person class
const person1 = new Person("Adam", 24, "Male");
const person2 = new Person("Gage", 30, "Male");
// Constructor Function (Pre-ES6)
function Car(brand, model, color) {
this.brand = brand;
this.model = model;
this.color = color;
}
// Creating instances of the Car constructor
const car1 = new Car("Mercedes", "S-Class", "Blue");
const car2 = new Car("Jaguar", "XE", "Black");
// Accessing properties of instances
console.log(person1.name); // Output: "Adam"
console.log(car1.model); // Output: "S-Class"
In this example, both the Person
class and the Car
constructor serve as blueprints for creating instances. The constructor
method (implicitly in classes, explicitly in constructors) sets the initial properties for the instances.
It's important to note that while the concepts and usage are similar, the syntax and capabilities of classes are more modern and flexible than traditional constructor functions. ES6 introduced classes as a more organized and syntactically convenient way to work with OOP concepts in JavaScript.
Certainly! It seems like you want to know about the structure of a class in JavaScript, including the constructor, prototype methods, and static methods. Here's a breakdown of each part:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// Prototype Method
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
// Static Method
static compareAges(person1, person2) {
if (person1.age > person2.age) {
return `${person1.name} is older than ${person2.name}.`;
} else if (person1.age < person2.age) {
return `${person2.name} is older than ${person1.name}.`;
} else {
return `${person1.name} and ${person2.name} are the same age.`;
}
}
}
Here's a breakdown of the components of the Person
class:
-
Constructor:
- The constructor method is used to create and initialize instances of the class.
- It's called automatically when a new instance is created using the
new
keyword. - In the example, the constructor takes
name
andage
parameters and assigns them to the instance's properties usingthis
.
-
Prototype Method (
greet
):- Prototype methods are added to the class's prototype, which means they are shared among all instances of the class.
- In the example, the
greet
method is defined, which logs a message with the person's name and age. - This method can be called on any instance of the
Person
class.
-
Static Method (
compareAges
):- Static methods are called on the class itself, rather than on instances of the class.
- In the example, the
compareAges
static method is defined to compare the ages of two people and return a message. - This method can be called on the
Person
class itself, without needing to create instances.
Here's how you would use the class:
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);
person1.greet(); // Output: Hello, my name is Alice and I am 30 years old.
person2.greet(); // Output: Hello, my name is Bob and I am 25 years old.
console.log(Person.compareAges(person1, person2)); // Output: Alice is older than Bob.
In this example, the class Person
encapsulates the properties (name, age) and behaviors (greeting and age comparison) of a person. The constructor initializes the properties, the greet
method is a shared behavior among instances, and the compareAges
method is a static utility method to compare the ages of different persons.
Inheritance in JavaScript
Inheritance is a fundamental concept in Object-Oriented Programming (OOP) that allows you to create a new class based on an existing class. The new class inherits properties and methods from the existing class, which is often referred to as the parent class or superclass. The new class is known as the subclass or child class.
Example:
Let's use an example to illustrate inheritance in JavaScript:
// Parent class (Superclass)
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// Child class (Subclass)
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // Call the parent class constructor
this.grade = grade;
}
study() {
console.log(`${this.name} is studying hard for their exams.`);
}
}
// Creating instances of the subclasses
const person = new Person("Alice", 30);
const student = new Student("Bob", 18, "12th");
// Using inherited methods and subclass-specific methods
person.greet(); // Output: Hello, my name is Alice and I am 30 years old.
student.greet(); // Output: Hello, my name is Bob and I am 18 years old.
student.study(); // Output: Bob is studying hard for their exams.
In this example:
- The
Person
class serves as the parent class. It has aconstructor
and agreet
method. - The
Student
class is the child class that extends thePerson
class using theextends
keyword. It adds agrade
property and astudy
method. - The
super()
method is used in theStudent
constructor to call the constructor of the parent class and pass the required parameters. - Instances of both classes are created and can access inherited methods (
greet
) as well as subclass-specific methods (study
).
By using inheritance, you can create a hierarchy of classes with shared behavior and properties, making your code more organized, reusable, and easier to maintain.
Let's create three classes: Person
, Student
, and Teacher
, to demonstrate inheritance and the usage of this
keyword. The Person
class will be the parent class, and both Student
and Teacher
will be subclasses that inherit from Person
. We'll also explore how to use the super
keyword to call the parent class's constructor.
// Parent class
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// Subclass: Student
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // Call parent class's constructor using super()
this.grade = grade;
}
study() {
console.log(`${this.name} is studying.`);
}
}
// Subclass: Teacher
class Teacher extends Person {
constructor(name, age, subject) {
super(name, age);
this.subject = subject;
}
teach() {
console.log(`${this.name} is teaching ${this.subject}.`);
}
}
// Creating instances
const person = new Person("Alice", 30);
const student = new Student("Bob", 18, "12th");
const teacher = new Teacher("Eve", 40, "Math");
// Using methods and properties
person.introduce(); // Output: Hello, my name is Alice and I am 30 years old.
student.introduce(); // Output: Hello, my name is Bob and I am 18 years old.
student.study(); // Output: Bob is studying.
teacher.introduce(); // Output: Hello, my name is Eve and I am 40 years old.
teacher.teach(); // Output: Eve is teaching Math.
Explanation:
-
The
Person
class is the parent class. It has a constructor that initializesname
andage
, and anintroduce
method that uses thethis
keyword to access the instance properties. -
The
Student
class is a subclass ofPerson
. It uses thesuper
keyword to call the parent class's constructor and sets thegrade
property. It also has astudy
method. -
The
Teacher
class is another subclass ofPerson
. It similarly calls the parent class's constructor usingsuper
and sets thesubject
property. It also has ateach
method. -
Instances of all three classes (
person
,student
, andteacher
) are created and methods from the parent and subclass are called.
In this example, the super
keyword is used to invoke the constructor of the parent class, and the this
keyword is used to access instance-specific properties and methods. Inheritance allows Student
and Teacher
to reuse properties and methods from the Person
class, demonstrating the concept of code reusability in object-oriented programming.
title: Prototype & Prototypal Inheritance description: Concept Prototype & Prototypal Inheritance in the OOP duration: 2100 card_type: cue_card
Prototype and Constructor Function
The concepts of prototype, constructor functions, and objects with an example of creating car objects with a shared prototype method.
In JavaScript, every function has a property called prototype
, which is an object that can be used as a blueprint for creating new objects.
// Constructor Function for Car
function Car(name, color) {
this.name = name;
this.color = color;
}
// Adding a shared prototype method
Car.prototype.drive = function() {
console.log(`${this.name} is driving.`);
};
Creating Objects with Constructor Function
Objects created using a constructor function share properties and methods defined in the prototype.
// Creating car objects using the constructor function
const car1 = new Car("Toyota", "Blue");
const car2 = new Car("Ford", "Red");
const car3 = new Car("Honda", "Silver");
// Using the shared prototype method
car1.drive(); // Output: Toyota is driving.
car2.drive(); // Output: Ford is driving.
car3.drive(); // Output: Honda is driving.
In this example:
- The
Car
constructor function defines propertiesname
andcolor
. - We add a shared prototype method
drive()
to theCar
constructor's prototype. - Three car objects (
car1
,car2
,car3
) are created using the constructor function. - All three car objects share the
drive()
method from the prototype.
Prototype Chain
The prototype chain allows objects to inherit properties and methods from their prototypes.
console.log(car1.hasOwnProperty("name")); // true
console.log(car1.hasOwnProperty("drive")); // false
- The
hasOwnProperty
method checks if a property is directly defined on the object. - The
name
property is directly defined on thecar1
object, sohasOwnProperty
returnstrue
. - The
drive
method is inherited from the prototype, sohasOwnProperty
returnsfalse
.
Absolutely, I'll break down the concept of prototypes and how they relate to constructor functions using the example of three car objects with a shared drive()
method.
Example - Car Objects:
// Constructor function for Car
function Car(name, color) {
this.name = name;
this.color = color;
}
// Adding a shared method to the Car constructor's prototype
Car.prototype.drive = function() {
console.log(`${this.name} is driving.`);
};
// Creating car instances
const car1 = new Car("Toyota", "Blue");
const car2 = new Car("Honda", "Red");
const car3 = new Car("Ford", "Black");
// Using the shared method
car1.drive(); // Output: Toyota is driving.
car2.drive(); // Output: Honda is driving.
car3.drive(); // Output: Ford is driving.
Explanation:
- The
Car
constructor function is created with parametersname
andcolor
, which set the properties of each car object. - The
drive
method is added to theCar
constructor's prototype. This means all instances ofCar
will have access to thedrive
method through inheritance. - Three car instances (
car1
,car2
, andcar3
) are created using theCar
constructor. - The
drive
method is called on each car instance, and it uses thethis
keyword to refer to the specific instance.
In this example, the drive
method is defined once on the prototype of the Car
constructor, and all car instances share this method. This approach reduces memory usage and makes the code more efficient since the method isn't duplicated for each instance.
Remember, prototypes and constructor functions are at the core of how JavaScript implements inheritance and code reuse.
Method shadowing
Method shadowing refers to the concept of overriding a method inherited from a parent object's prototype by defining a method with the same name in the child object. In your context of constructor functions and prototypes, let's explore method shadowing using the car example:
// Constructor function for Car
function Car(name, color) {
this.name = name;
this.color = color;
}
// Adding a shared method to the Car constructor's prototype
Car.prototype.drive = function() {
console.log(`${this.name} is driving.`);
};
// Child constructor function for ElectricCar
function ElectricCar(name, color, batteryType) {
Car.call(this, name, color); // Call the parent constructor to set name and color
this.batteryType = batteryType;
}
// Inherit from Car prototype
ElectricCar.prototype = Object.create(Car.prototype);
// Adding a method specific to ElectricCar
ElectricCar.prototype.charge = function() {
console.log(`${this.name} is charging.`);
};
// Method shadowing in ElectricCar
ElectricCar.prototype.drive = function() {
console.log(`${this.name} is driving silently.`);
};
// Creating car instances
const car1 = new Car("Toyota", "Blue");
const eCar1 = new ElectricCar("Tesla", "Black", "Lithium-ion");
// Using the drive method
car1.drive(); // Output: Toyota is driving.
eCar1.drive(); // Output: Tesla is driving silently.
Explanation:
- We have the
Car
constructor with a shareddrive
method. - We create a child constructor
ElectricCar
that inherits properties fromCar
usingCar.call(this, name, color)
. - We set up inheritance for the
ElectricCar
prototype usingObject.create(Car.prototype)
. - We add a specific method
charge
forElectricCar
. - We use method shadowing by defining a new
drive
method inElectricCar
prototype, which overrides the inheriteddrive
method.
In the example, ElectricCar
inherits the drive
method from Car
. However, by defining a new drive
method in ElectricCar
, the inherited method is shadowed or overridden. This is a way to provide specialized behavior for a method in a subclass while still utilizing the inheritance structure.
Remember, method shadowing allows you to override inherited methods, giving you flexibility to adapt and extend functionality in child classes.