Introducing Object-Oriented Programming

Objectives

After completing this lesson, you will be able to:

  • Identify the main concepts of object-oriented programming (OOP)

Introduction to Object-Oriented Programming (OOP)

OOPs Concepts

JavaScript is an object-oriented programming language, but it uses a different model than most other object-oriented languages, such as Java or C#. JavaScript uses prototypes instead of classes for inheritance, which is a form of object-oriented inheritance that relies on shared objects.

Here are the main concepts of object-oriented programming in JavaScript:

  • Objects: In JavaScript, an object is a standalone entity, with properties and type. It can be seen as a container that holds related data and methods used to manipulate that data. You can create an object using the object literal syntax or the new keyword.
  • Properties: A JavaScript object has properties associated with it. A property of an object can be explained as a variable that is attached to the object.
  • Methods: Methods are functions that are associated with an object. They define the behaviors or functionalities that an object can perform.
  • Prototype Inheritance: In JavaScript, each object has a prototype object, which acts as a template object from which it inherits methods and properties. An object’s prototype object may also have a prototype object, from which it inherits methods and properties, and so on. This is often referred to as a prototype chain.
  • Constructors: In JavaScript, the constructor method is a special method for creating and initializing an object. A constructor can use the this keyword to reference the object that will be created and initialized.
  • thisKeyword: In JavaScript, this is a special keyword that is used in methods to refer to the object on which a method is called. It's a reference to the object the method was called upon.
  • Encapsulation: This is the practice of keeping fields within a JavaScript object private, so they can only be accessed and modified through methods of their containing object. This is usually done by defining them as local variables within the object constructor.
  • Polymorphism: While JavaScript doesn't support polymorphism in the traditional sense due to lack of type-checking, it can achieve a similar outcome because it's dynamically typed. This means you can overwrite or change methods and properties in a way that's more flexible than in statically typed languages.
Note
Note that with the introduction of ES6 (ECMAScript 2015), JavaScript introduced a class syntax that allows developers to write classes that work much like they do in other object-oriented languages. However, it's important to understand that under the hood, this is just syntactic sugar, and JavaScript still uses prototypes for inheritance.

Object Prototypes

Introduction

In JavaScript, almost everything is an object, and every object has a special internal property called [[Prototype]]. This property is a link to another object, the object's prototype. When trying to access a property that does not exist in an object, JavaScript will look for that property in the object's prototype. This is known as prototype chaining.

Prototype Chaining

Prototype chaining allows objects to "inherit" properties from their prototype objects. Here's an example.

Code snippet
let animal = {
eats: true
};


let rabbit = {
jumps: true,
__proto__: animal // sets rabbit's prototype to be the animal object
};


console.log(rabbit.eats); // true
Expand

In this example, rabbit doesn't have the eats property itself. However, when we call rabbit.eats, JavaScript checks the rabbit object, doesn't find the eats property, then moves up to the rabbit's prototype (which is animal) and finds the eats property there.

Constructor Function and prototype Property

JavaScript provides constructor functions for creating new objects. The prototype property in JavaScript is a pre-existing property on constructor functions that gives us a place to add methods and properties that are shared across all instances of a constructor function.

Code snippet
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}


Person.prototype.sayFullName = function() {
return this.firstName + " " + this.lastName;
}


let person1 = new Person("John", "Doe");
console.log(person1.sayFullName()); // "John Doe"
Expand

Here, sayFullName is a method on the Person.prototype that all instances of Person can access.

Prototypes are Shared Among All Instances

When a method is added to the prototype, that method is shared among all instances. This is efficient because it saves memory. The method isn't duplicated for each instance. Instead, each instance references the method located on the prototype.

Prototype Chain and Inheritance

One powerful feature of prototypes in JavaScript is the ability to simulate inheritance, which is a key aspect of OOP.

Code snippet
function Animal(name) {
this.name = name;
}


Animal.prototype.eat = function() {
console.log(this.name + ' eats.');
}


function Rabbit(name) {
this.name = name;
}


Rabbit.prototype = Object.create(Animal.prototype); // Rabbit now inherits from Animal
Rabbit.prototype.jump = function() {
console.log(this.name + ' jumps.');
}


let rabbit = new Rabbit('Bunny');
rabbit.eat(); // "Bunny eats."
rabbit.jump(); // "Bunny jumps."
Expand

Here, Rabbit inherits from Animal by setting Rabbit.prototype to Object.create(Animal.prototype). As a result, a Rabbit instance can access methods defined in Animal.prototype.

Conclusion

Understanding prototypes in JavaScript is crucial because it is a fundamental part of the language. It allows for efficient memory usage through shared methods, and it enables inheritance, a key aspect of OOP.

Class

Introduction

In JavaScript, classes are a way to create objects with specific properties and methods. They are essentially syntactic sugar over JavaScript's existing prototype-based inheritance. The class syntax doesn't introduce a new object-oriented inheritance model to JavaScript, but provides a much simpler and cleaner way to create objects and deal with inheritance.

Defining and Instantiating Classes

To define a class, you use the class keyword followed by the name of the class.

Refer to the following code snippet.

Code snippet
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Expand

The constructor method is a special method for creating and initializing objects created with a class. It runs automatically when a new object is created.

To create a new instance of a class, you use the new keyword.

Refer to the following code snippet.

Code snippet
let rectangle = new Rectangle(10, 5);
console.log(rectangle.height); // 10
console.log(rectangle.width); // 5
Expand

Class Methods

You can define methods on the class which can be called on instances of the class.

Refer to the following code snippet.

Code snippet
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}


area() {
return this.height * this.width;
}
}


let rectangle = new Rectangle(10, 5);
console.log(rectangle.area()); // 50
Expand

In the example, area is a method that calculates the area of the Rectangle.

Getters and Setters

Getters and setters are special types of class methods used to get and set the properties of an object.

Code snippet
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}


get area() {
return this.height * this.width;
}


set height(value) {
if (value <= 0) {
console.log("Height must be a positive number.");
} else {
this.height = value;
}
}
}


let rectangle = new Rectangle(10, 5);
console.log(rectangle.area); // 50


rectangle.height = -10; // "Height must be a positive number."
Expand

In the example, area is a getter method and height is a setter method.

Inheritance with Classes

Classes can also inherit from other classes. The extends keyword is used to create a class that is a child of another class.

Code snippet
class Shape {
constructor(name) {
this.name = name;
}


display() {
return `This is a ${this.name}.`;
}
}


class Rectangle extends Shape {
constructor(height, width) {
super('Rectangle');
this.height = height;
this.width = width;
}


area() {
return this.height * this.width;
}
}


let rectangle = new Rectangle(10, 5);
console.log(rectangle.display()); // "This is a Rectangle."
console.log(rectangle.area()); // 50
Expand

In this example, Rectangle is a subclass of Shape. The super keyword is used to call the constructor of the parent class.

Conclusion

Classes in JavaScript provide a simpler and more intuitive way to deal with objects and inheritance. They allow for a cleaner and more readable syntax for generating object instances and handling prototype-based inheritance. Understanding classes and how they work is fundamental to mastering JavaScript.

Log in to track your progress & complete quizzes