JavaScript Object-Oriented Programming: The Basics

JavaScript Object-Oriented Programming: The Basics

Object-Oriented Programming (OOP) is a way of writing code that models real-world things or ideas as objects. These objects group together data (called properties) and actions (called methods) that belong together. This method of programming helps us organize code clearly and makes it easier to manage complexity by thinking in terms of objects rather than just lines of code. In JavaScript, OOP is a core part of the language, and it allows developers to create reusable and modular code using objects and classes.

JavaScript supports OOP in several ways, including simple objects, constructor functions, prototypes, and the modern class syntax introduced in ES6. Understanding these tools lets you write programs that mimic real-life behaviors and relationships, such as animals with different abilities or vehicles with varying speeds. This article will take you through how to write OOP in JavaScript step-by-step, with fun and engaging examples to bring concepts to life.

Creating Objects Using Object Literals

One of the simplest ways to create an object in JavaScript is by using an object literal. This is a straightforward way to group related data and functions together inside curly braces {}.

For example, imagine we want to represent a dog with a name and a way to bark:

const dog = {

  name: "Kol",
  bark: function() {
    console.log(this.name + " says: Woof!");
  }

};

dog.bark();

Here, we create an object named dog with a property name set to "Kol". It also has a method bark that logs a message using the dog’s name. When we run dog.bark(), it prints “Kol says: Woof!” to the console. This example shows how objects combine data (name) and behavior (bark) neatly.

Using Constructor Functions

When we want to create many similar objects, writing object literals repeatedly becomes tiresome. JavaScript uses constructor functions as blueprints for creating objects with similar structure.

Consider a constructor function to make cats:

function Cat(name) {

  this.name = name;

  this.meow = function() {
    console.log(this.name + " says: Meow!");
  };

}

const kitty = new Cat("Whiskers");
kitty.meow();

The Cat function acts like a template. Using the new keyword creates a new cat object with the given name. Calling kitty.meow() runs the method, printing “Whiskers says: Meow!”. This shows how constructors help us produce many objects with similar properties and methods quickly.

Using Prototypes to Share Methods

One downside to the previous approach is that each cat object has its own copy of the meow function, wasting memory. JavaScript solves this with prototypes, which allow objects to share methods.

Here’s how to add a shared method via prototype:

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

Cat.prototype.meow = function() {
  console.log(this.name + " says: Meow from prototype!");
};

const kitty1 = new Cat("Snowball");
const kitty2 = new Cat("Fluffy");

kitty1.meow();
kitty2.meow();

In this example, meow is added to Cat.prototype, so all cats share the same method instead of each having their own. Both kitty1 and kitty2 can call meow(), and it prints the message including their name. This saves memory and keeps methods consistent across objects.

Introducing ES6 Classes

To make OOP easier, ES6 introduced the class syntax, which looks cleaner and is simpler to write than constructor functions and prototypes.

Let’s create a Bird class with a fly method:

class Bird {

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

  fly() {
    console.log(this.name + " is flying high!");
  }

}

const tweety = new Bird("Tweety");
tweety.fly();

Here, the class keyword defines a blueprint for birds. The constructor method sets the bird’s name. The fly method belongs to the class and can be used by all instances. Running tweety.fly() logs “Tweety is flying high!”. Classes hide the complexity of prototypes and are the modern way to write OOP in JavaScript.

Inheritance with Classes

OOP is powerful because objects can inherit properties and methods from others. This is called inheritance. In JavaScript classes, inheritance is done with the extends keyword.

Suppose we want to create a Parrot class that inherits from Bird and adds a new method:

class Bird {

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

  fly() {
    console.log(this.name + " is flying high!");
  }

}

class Parrot extends Bird {

  talk() {
    console.log(this.name + " says: Polly wants a cracker!");
  }

}

const polly = new Parrot("Polly");
polly.fly();
polly.talk();

Parrot inherits the fly method from Bird. It also has its own method, talk. So, polly.fly() prints “Polly is flying high!”, and polly.talk() prints the fun parrot phrase. This shows how subclasses can reuse and extend behavior from parent classes.

Using Getters and Setters

JavaScript classes allow special methods called getters and setters to control how properties are accessed and changed.

Consider a Person class with firstName and lastName, and a getter for fullName:

class Person {

  constructor(firstName, lastName) {

    this.firstName = firstName;
    this.lastName = lastName;

  }

  get fullName() {
    return this.firstName + " " + this.lastName;
  }

  set fullName(name) {

    const parts = name.split(" ");
    this.firstName = parts[0];
    this.lastName = parts[1];

  }

}

const edward = new Person("Edward", "Mwamba");
console.log(edward.fullName);

edward.fullName = "Stephen Mwamba";
console.log(edward.firstName);
console.log(edward.lastName);

Here, fullName acts like a property but runs code when accessed or changed. We get “Edward Mwamba” printed, then update the name using the setter, and finally print the new first and last names. Getters and setters make working with object data cleaner and more controlled.

Writing a Fun Example: A Simple Animal Race

To combine everything, let’s create a small program simulating a race between animals using classes, inheritance, and methods.

class Animal {

  constructor(name, speed) {
    this.name = name;
    this.speed = speed; // speed in km/h
  }

  run() {
    console.log(this.name + " runs at " + this.speed + " km/h.");
  }

}

class Dog extends Animal {

  bark() {
    console.log(this.name + " barks: Woof!");
  }

}

class Cat extends Animal {

  meow() {
    console.log(this.name + " meows: Meow!");
  }

}

const dog = new Dog("Rex", 45);
const cat = new Cat("Luna", 40);

dog.run();
dog.bark();

cat.run();
cat.meow();

if (dog.speed > cat.speed) {
  console.log(dog.name + " wins the race!");
} else if (cat.speed > dog.speed) {
  console.log(cat.name + " wins the race!");
} else {
  console.log("It's a tie!");
}

In this example, we define an Animal class with name and speed, and a method to show the speed. Dog and Cat extend Animal and add their own sounds. We create a dog and a cat, have them run and make sounds, and finally compare speeds to decide who wins the race. This playful example shows how OOP can model real-world things and interactions clearly and fun.

Conclusion

In this introduction to JavaScript Object-Oriented Programming, we explored multiple ways to create and organize objects. Starting with simple object literals, we moved to constructor functions and prototypes to share methods efficiently. The modern class syntax made writing and understanding object structures easier. We saw inheritance allowing new classes to reuse and extend behavior, and we used getters and setters to handle properties cleanly. Finally, a fun animal race example showed how all these pieces work together.

Mastering these OOP concepts in JavaScript gives you powerful tools to design code that reflects real-world relationships and behaviors, making your programs organized and easier to manage.

References

If you want to learn more about JavaScript OOP and related concepts, these resources are excellent starting points:

Scroll to Top