[Roadmap_OOP] 1_JavaScript Fundamentals

Table of content

Introduction

Here’s a quick rundown of the foundational OOP concepts in JavaScript:

1. Objects:

2. Classes:

3. Inheritance:

4. Encapsulation:

5. Polymorphism:

These are the core concepts of OOP in JavaScript. While JavaScript has its own way of implementing them compared to stricter OOP languages, understanding these principles will help you write more organized, reusable, and maintainable code.

Objects

In JavaScript, objects are fundamental for structuring data and organizing code. They act as blueprints for creating more complex entities and are essential for object-oriented programming (OOP).

Here’s a breakdown of key concepts about objects in JavaScript:

What are Objects?

Creating Objects:

Accessing Properties:

Methods:

Mutability:

Importance of Objects:

Classes

Classes in JavaScript provide a structured way to create objects. They act like blueprints that define the properties and methods that objects of a certain kind will share.

Creating Classes:

JavaScript uses the class keyword to define classes. Here’s the basic syntax:

class ClassName {
  // Constructor function (optional)
  constructor(argument1, argument2, ...) {
    // Initialize properties
    this.property1 = argument1;
    this.property2 = argument2;
    // ...
  }

  // Methods (functions defined inside the class)
  methodName(argument1, argument2, ...) {
    // Method implementation
  }
}

Explanation:

Example: Creating a Person Class

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log("Hello, my name is " + this.name);
  }
}

// Creating objects (instances) of the Person class
let person1 = new Person("Alice", 30);
let person2 = new Person("Bob", 25);

person1.greet(); // Output: "Hello, my name is Alice"
person2.greet(); // Output: "Hello, my name is Bob"

Key Points:

Inheritance:

JavaScript supports inheritance, which allows you to create new classes (subclasses) that inherit properties and methods from existing classes (superclasses). This promotes code reusability and helps organize complex object hierarchies.

Inheritance

Inheritance is a fundamental concept in object-oriented programming (OOP) that allows you to create hierarchical relationships between classes. It’s about code reusability and building upon existing functionality.

Here’s how inheritance works:

Benefits of Inheritance:

JavaScript and Classes:

JavaScript uses classes to create objects with blueprints. While it doesn’t directly implement inheritance like Java or C++, it achieves a similar effect using prototypes. Here’s how it works:

  1. Classes define the blueprint: A class defines the properties and methods that objects of that class will have.
  2. extends keyword for inheritance: The extends keyword is used in the subclass declaration to specify the superclass it inherits from.
  3. Prototype chain: In JavaScript, objects are linked together through a prototype chain. When you create a new object from a class, it inherits the properties and methods from the class’s prototype. The superclass prototype is then linked in the chain, allowing the subclass to access inherited properties and methods.

Example: Inheritance with Shapes

class Shape {
  constructor(color) {
    this.color = color;
  }

  getArea() {
    // Implement area calculation for specific shapes (abstract in base class)
    throw new Error("getArea() is not implemented in Shape class!");
  }
}

class Rectangle extends Shape {
  constructor(color, width, height) {
    super(color); // Call superclass constructor to inherit color
    this.width = width;
    this.height = height;
  }

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

class Circle extends Shape {
  constructor(color, radius) {
    super(color);
    this.radius = radius;
  }

  getArea() {
    return Math.PI * Math.pow(this.radius, 2);
  }
}

let rectangle = new Rectangle("red", 5, 4);
let circle = new Circle("blue", 3);

console.log(rectangle.getArea()); // Output: 20
console.log(circle.getArea()); // Output: 28.27...

In this example, Shape is the superclass defining the color property and a generic getArea() method. Rectangle and Circle inherit from Shape, adding their specific properties and overriding the getArea() method to calculate their respective areas.

Remember, while JavaScript uses prototypes for inheritance, the class syntax with extends makes it look more similar to traditional OOP languages.

Encapsulation

Encapsulation in JavaScript’s OOP context refers to the practice of bundling data (properties) and the methods that operate on that data within a single object. The goal is to potentially restrict direct access to some properties, promoting data protection and controlled modification.

Key Points:

Example: Bank Account with Encapsulation

class BankAccount {
  constructor(accountNumber, balance) {
    this._accountNumber = accountNumber; // Private property with underscore prefix
    this.balance = balance; // Public property (can be accessed directly)
  }

  deposit(amount) {
    if (amount > 0) {
      this.balance += amount;
    } else {
      console.error("Deposit amount must be positive");
    }
  }

  withdraw(amount) {
    if (amount > 0 && amount <= this.balance) {
      this.balance -= amount;
    } else {
      console.error("Insufficient funds or invalid withdrawal amount");
    }
  }

  // Getter method to provide controlled access to private account number
  get accountNumber() {
    return this._accountNumber.slice(3); // Maybe only reveal last 3 digits for security
  }
}

let account = new BankAccount(1234567890, 100);

// Direct access to private property (not recommended)
console.log(account._accountNumber); // Output: 1234567890 (full number exposed)

// Public method for deposit
account.deposit(50);
console.log(account.balance); // Output: 150

// Public method for withdrawal
account.withdraw(20);
console.log(account.balance); // Output: 130

// Accessing private property through getter method (recommended)
console.log(account.accountNumber); // Output: 7890 (only last 4 digits revealed)

Explanation:

Remember:

Polymorphism

Polymorphism in JavaScript’s OOP context refers to the ability of objects from different classes to respond to the same method call in distinct ways. It allows for flexible and dynamic behavior in your code.

Key Points:

Example: Animal Sounds with Polymorphism

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

  makeSound() {
    console.log("Generic animal sound");
  }
}

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

  makeSound() {
    console.log(this.name + " barks!");
  }
}

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

  makeSound() {
    console.log(this.name + " meows!");
  }
}

let animals = [new Dog("Snoopy"), new Cat("Garfield"), new Animal("Unknown")];

function playSound(animal) {
  if (typeof animal.makeSound === "function") {
    // Duck Typing check
    animal.makeSound();
  } else {
    console.log("Animal doesn't have a makeSound method");
  }
}

for (let animal of animals) {
  playSound(animal);
}

Explanation:

  1. The Animal class defines a generic makeSound method.
  2. Dog and Cat inherit from Animal and override the makeSound method with their specific sounds.
  3. The playSound function takes any animal object.
  4. It uses duck typing to check if the object has a makeSound method (regardless of the class).
  5. If the method exists, it’s called, resulting in the appropriate sound based on the object’s actual class (Dog or Cat).

Benefits of Polymorphism:

Remember:

Conclusion

We have learned about the fundamental concepts in OOP for javascript, many of these concepts will be seen more in practice, but its important to be aware of them on a concept level to apply them in a more efficient manner.

See you on the next post.

Sincerely,

Eng. Adrian Beria