Object-Oriented Programming in JavaScript

In this lesson, we’ll explore how JavaScript handles object-oriented programming (OOP) and compare it with Java’s approach. As a Java developer, you’re already familiar with class-based OOP, but JavaScript’s prototypal inheritance model might be new to you. We’ll cover both the traditional prototypal approach and the more recent ES6 class syntax, which should feel more familiar to you.

Prototypal Inheritance vs. Class-based Inheritance

Java uses class-based inheritance, where objects are instances of classes. JavaScript, on the other hand, traditionally uses prototypal inheritance. In this model, objects inherit directly from other objects.

Let’s look at a simple example:

// JavaScript
const animal = {
    makeSound: function() {
        console.log("Some generic animal sound");
    }
};

const dog = Object.create(animal);
dog.bark = function() {
    console.log("Woof!");
};

dog.makeSound(); // Outputs: "Some generic animal sound"
dog.bark();      // Outputs: "Woof!"

In this example, dog inherits from animal through the prototype chain. This is fundamentally different from Java’s class-based inheritance, where you would define a Dog class that extends an Animal class.

ES6 Class Syntax

To make JavaScript more accessible to developers coming from class-based languages like Java, ES6 introduced a class syntax. This syntax is syntactic sugar over JavaScript’s existing prototype-based inheritance.

Here’s how you might define a class in JavaScript:

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

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

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

    bark() {
        console.log("Woof!");
    }
}

const myDog = new Dog("Buddy");
myDog.makeSound(); // Outputs: "Some generic animal sound"
myDog.bark();      // Outputs: "Woof!"

This should look more familiar to you as a Java developer. However, it’s important to understand that under the hood, JavaScript is still using prototypal inheritance.

Constructor Functions and the ‘new’ Keyword

Before the introduction of the class syntax, JavaScript used constructor functions to create objects. This approach is still valid and widely used:

// JavaScript
function Animal(name) {
    this.name = name;
}

Animal.prototype.makeSound = function() {
    console.log("Some generic animal sound");
};

function Dog(name) {
    Animal.call(this, name);
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
    console.log("Woof!");
};

const myDog = new Dog("Buddy");
myDog.makeSound(); // Outputs: "Some generic animal sound"
myDog.bark();      // Outputs: "Woof!"

The new keyword in JavaScript serves a similar purpose to Java’s new, but it’s important to note that in JavaScript, it’s used with constructor functions rather than class definitions (even though the class syntax uses new as well).

Inheritance Using extends and super

The extends keyword in JavaScript’s class syntax works similarly to Java:

// JavaScript
class Vehicle {
    constructor(wheels) {
        this.wheels = wheels;
    }

    drive() {
        console.log(`Driving with ${this.wheels} wheels`);
    }
}

class Car extends Vehicle {
    constructor(brand) {
        super(4);
        this.brand = brand;
    }

    honk() {
        console.log(`${this.brand} car honking`);
    }
}

const myCar = new Car("Toyota");
myCar.drive(); // Outputs: "Driving with 4 wheels"
myCar.honk();  // Outputs: "Toyota car honking"

The super keyword is used to call the parent class constructor and to access parent class methods, just like in Java.

Encapsulation and Private Fields

JavaScript has traditionally lacked true private fields, but there have been conventions to simulate privacy. With the recent addition of the # prefix, JavaScript now supports true private fields:

// JavaScript
class BankAccount {
    #balance = 0;  // Private field

    deposit(amount) {
        if (amount > 0) {
            this.#balance += amount;
        }
    }

    getBalance() {
        return this.#balance;
    }
}

const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // Outputs: 100
console.log(account.#balance);     // Syntax Error: Private field

This # syntax for private fields is a recent addition to JavaScript and may not be supported in older environments.

Conclusion

In this lesson, we’ve explored how JavaScript handles object-oriented programming. We’ve seen how its prototypal inheritance model differs from Java’s class-based approach, and how the ES6 class syntax provides a more familiar interface for developers coming from languages like Java. We’ve also looked at constructor functions, inheritance, and the concept of private fields in JavaScript.

In the next lesson, we’ll dive into functional programming concepts in JavaScript, exploring how JavaScript’s treatment of functions as first-class citizens opens up powerful programming paradigms that might be new to you as a Java developer.