Object-oriented programming

Object-oriented programming

What is it?

In JavaScript, object-oriented programming (OOP) is a programming paradigm that is based on the concept of "objects", which can contain data and code that manipulates that data, the OOP programming paradigm is fundamental to many languages and not limited to javascript alone and going forward I will be explaining the (OOP) programming paradigm using JavaScript while covering few terms like classes and instance, Inheritance, Encapsulation, Polymorphism and Abstraction with code samples demonstrating each term.

Classes and instances

Objects are created from "classes", which are templates that define the properties and behaviours of objects.

JavaScript uses prototypal inheritance, which means that objects can inherit properties and behaviours from other objects. This is different from classical inheritance, which is used in languages like Java and C++.

In JavaScript, a class is a blueprint for creating objects. It defines the properties and methods that will be available on the objects that are created from the class.

Here is an example of a simple class in JavaScript:

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.`);
  }
}

To create an object from a class, you can use the new keyword followed by the class name. This is called instantiating the class.


// Instantiate the `Person` class
const person= new Person('Alice', 30);

The person object is an instance of the Person class. It has the properties and methods defined in the Person class, as well as any additional properties or methods that you add to the object.

You can access and modify the properties of an instance using dot notation or bracket notation:

console.log(person.name(); // Output: "Alice");
person.age = 31;
console.log(person['age']); // Output: 31

You can also call the methods of an instance using dot notation:

person.greet(); // Output: `Hello, my name is Alice and I am 31 years old.

Inheritance

Inheritance is a concept in object-oriented programming that allows one class to inherit the properties and methods of another class. In JavaScript, inheritance is implemented using prototypes.

Here is an example of a base class and a derived class that inherits from the base class:

class Animal {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    console.log(`Hello, I am ${this.name} the animal`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name);
  }

  bark() {
    console.log('Woof woof!');
  }
}

In the example above, the Dog class is derived from the Animal class. It inherits the name property and the sayHello method from the Animal class. The Dog class also has its own bark method which is specific to instances of the Dog class.

To create an instance of the Dog class, you can use the new keyword followed by the class name, just as you would with any other class:

const broxi = new Dog('Broxi');

You can access the inherited properties and methods using dot notation:

console.log(broxi.name); // Output: "Broxi"
broxi.sayHello(); // Output: "Hello, I am Broxi the animal"

You can also call the class-specific method using dot notation:

broxi.bark(); // Output: "Woof woof!"

Encapsulation

Encapsulation is a concept in object-oriented programming that refers to the bundling of data and methods that operate on that data within a single unit, or object. encapsulation is also used to hide the implementation details of a class from other objects and protect the internal state of an object from being modified by external code. In JavaScript, encapsulation is achieved through the use of classes and objects.

Here is an example of a class that uses encapsulation to protect the internal state of an object:

class BankAccount {
  constructor(balance) {
    this._balance = balance;
  }

  get balance() {
    return this._balance;
  }

  set balance(amount) {
    if (amount < 0) {
      throw new Error('Cannot set balance to a negative amount');
    }
    this._balance = amount;
  }

  deposit(amount) {
    this.balance += amount;
  }

  withdraw(amount) {
    this.balance -= amount;
  }
}

In this example, the BankAccount class has a private property called _balance that stores the balance of the account. The balance getter and setter methods allow you to access and modify the balance, but they also include validation to ensure that the balance cannot be set to a negative amount.

To create an instance of the BankAccount class, you can use the new keyword followed by the class name:

const account = new BankAccount(1000);

You can access the balance using the balance getter method:

console.log(account.balance); // Output: 1000

You can modify the balance using the balance setter method, as long as you pass a valid amount:

account.balance = 500;
console.log(account.balance); // Output: 500

You can also use the deposit and withdraw methods to add or remove funds from the account:

account.deposit(250);
console.log(account.balance); // Output: 750
account.withdraw(100);
console.log(account.balance); // Output: 650

Polymorphism

Polymorphism is a concept in object-oriented programming that refers to the ability of a single object to take on multiple forms. In JavaScript, polymorphism is achieved through inheritance and method overriding.

Here is an example of polymorphism in JavaScript using inheritance and method overriding:

class Animal {
  constructor(name) {
    this.name = name;
  }

  makeSound() {
    console.log('Some generic animal sound');
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name);
  }

  makeSound() {
    console.log('Woof woof!');
  }
}

class Cat extends Animal {
  constructor(name) {
    super(name);
  }

  makeSound() {
    console.log('Meow meow!');
  }
}

In this example, the Animal class has a makeSound method that is overridden by the Dog and Cat classes. This allows each class to have its own implementation of the makeSound method, resulting in different behaviour when the method is called on an instance of each class.

To create an instance of one of the derived classes, you can use the new keyword followed by the class name, just as you would with any other class:

const broxi = new Dog('Broxi');
const angus = new Cat('Angus');

You can call the makeSound method on each instance to see the polymorphic behaviour:

broxi.makeSound(); // Output: "Woof woof!"
angus.makeSound(); // Output: "Meow meow!"

Abstraction

In JavaScript or any other programming language, abstraction refers to the act of representing essential features without including the background details or implementation. It allows you to focus on what the object does rather than how it does it.

There are several ways to achieve abstraction in JavaScript, including:

  1. Functions: Functions can be used to abstract away complex logic and provide a simple interface for interacting with that logic.

  2. Objects: Objects can be used to group related functionality together and provide a simple interface for accessing that functionality.

  3. Classes: Classes provide a way to define objects with similar behavior and create instances of those objects. This allows developers to create reusable code and abstract away the details of object creation.

  4. Modules: Modules allow developers to organize their code into self-contained units that can be imported and used in other parts of the application. This helps to abstract away the details of the implementation and make it easier to use and maintain the code.

Overall, abstraction is an important concept in JavaScript (and in programming in general) as it allows developers to create complex functionality in a way that is easy to use and maintain, and helps to improve the structure and organization of code.

An example of abstraction in JavaScript using functions:

function add(x, y) {
  return x + y;
}

function subtract(x, y) {
  return x - y;
}

function multiply(x, y) {
  return x * y;
}

function divide(x, y) {
  return x / y;
}

In the above example, the add, subtract, multiply, and divide functions provide a simplified interface for performing basic arithmetic operations. The implementation details of these operations are abstracted away, and the functions can be used without the user needing to know how the operations are actually performed.

An example code showing abstraction in play using objects:

const calculator = {
  add(x, y) {
    return x + y;
  },
  subtract(x, y) {
    return x - y;
  },
  multiply(x, y) {
    return x * y;
  },
  divide(x, y) {
    return x / y;
  }
};

// Use the calculator object
console.log(calculator.add(2, 3)); // 5
console.log(calculator.subtract(5, 2)); // 3
console.log(calculator.multiply(4, 2)); // 8
console.log(calculator.divide(9, 3)); // 3

In the example above, the calculator object provides a simplified interface for performing arithmetic operations. The implementation details of these operations are encapsulated within the object, and the object's methods can be used without the user needing to know how the operations are actually performed.

Below is an example of abstraction in JavaScript using modules:

// File: math.js
export function add(x, y) {
  return x + y;
}

export function subtract(x, y) {
  return x - y;
}

export function multiply(x, y) {
  return x * y;
}

export function divide(x, y) {
  return x / y;
}

// File: main.js
import * as math from './math';

console.log(math.add(2, 3)); // 5
console.log(math.subtract(5, 2)); // 3
console.log(math.multiply(4, 2)); // 8
console.log(math.divide(9, 3)); // 3

In this example, the math module exports a set of functions that provide a simplified interface for performing arithmetic operations. The implementation details of these operations are encapsulated within the module, and the functions can be imported and used without the user needing to know how the operations are actually performed.

Modules provide a way to organize code into self-contained units that can be imported and used in other parts of the application, which helps to improve the structure and organization of the code and abstract away the details of the implementation.

The snippet below is an example of abstraction in JavaScript using classes:

class Calculator {
  add(x, y) {
    return x + y;
  }

  subtract(x, y) {
    return x - y;
  }

  multiply(x, y) {
    return x * y;
  }

  divide(x, y) {
    return x / y;
  }
}

const calc = new Calculator();

console.log(calc.add(2, 3)); // 5
console.log(calc.subtract(5, 2)); // 3
console.log(calc.multiply(4, 2)); // 8
console.log(calc.divide(9, 3)); // 3

In the example above, the Calculator class defines a set of methods that provide a simplified interface for performing arithmetic operations. The implementation details of these operations are encapsulated within the class, and instances of the class can be created and used without the user needing to know how the operations are actually performed.

Classes provide a way to define objects with similar behavior and create instances of those objects, which allows developers to create reusable code and abstract away the details of object creation.

Thank you for finding time to read and I hope the article did shed light on the topic for you feel free to leave questions in the comment

Did you find this article valuable?

Support Ayobami Haastrup | Web Design &amp; Seo Tips | Blog | Northampton, UK by becoming a sponsor. Any amount is appreciated!