Encapsulation means keeping something isolated. If you put something in a capsule, the outside world can't access it. Encapsulation is an important concept in object-oriented programming because it helps keep complex code manageable.

Why You Need Classes

Let's say you have a petting zoo app with hundreds of thousands of lines of code. Now imagine that there's a highly important object that is central to the whole application, called animal. What if every single part of the program that was an animal could access and change that object?

Unrestricted access would cause a lot of chaos. If a piglet uses animal to define its parameters, then animal will have piglet attributes. Now, let's say a goat decides to use animal to define its parameters.

In JavaScript/TypeScript, that would look like this:

            var animal = {name: "piglet", legs: 4, color: "pink", decoration: "snout"}
animal.name = "goat"
animal.decoration = "horns"

    

Next thing you know, you've got pink goats and piglets with horns. See the code in action at the TypeScript sandbox then click run to view console output.

If you're learning to program and want inspiration besides creating a petting zoo, here are 10 more projects to inspire you.

Because your codebase is so huge, it could take hundreds of hours to find the code that's giving your lambs llama necks and your ducklings wool. And once you do find the offending code, you will have to write even more spaghetti code to keep the objects from interfering with each other. There must be a better way.

The way to fix the overlap problem is by defining objects with classes. Any part of the code can create an object based on the class definition. Creating a unique object is called instantiation. It guarantees that every object created will have its own properties. And those objects won't be able to interfere with each other accidentally.

Classes Aren't Enough; Your Object Variables Need Encapsulation Too

So we've decided that every animal needs its own object. Let's create a class that will define our animals.

            class Animal {
  name: string;
  legs: number;
  color: string;
  decoration: string;

  constructor(name: string, legs: number, color: string, decoration: string) {
    this.name = name;
    this.legs = legs;
    this.color = color;
    this.decoration = decoration;
  }
}

    

Next, let's create a couple of animal objects.

            let babyDuck = new Animal("baby duck", 2, "yellow", "beak");
let bunny = new Animal("bunny", 4, "gray", "floppy ears");

    

Play with the code so far.

Now we can add all of the animals we want without any weird mutations. Or can we?

a gray and white bunny in a grassy field

What would happen if one night, a tired programmer wrote some code to edit an animal from the creepy-crawly app, but they edited the bunny by mistake?

            bunny.color = "black";
bunny.legs = 8;

    

Spider bunnies are not cool, man! That's just as bad as when we didn't encapsulate our code into objects. Let's make sure that never happens again.

The first thing we need to do is to make our objects private. That means that nothing can edit our variables directly after creating them. Here's the code showing that changing private variables creates an error.

Variables do need to be changeable, though. And that's where getters and setters come in.

Getters and setters are functions that access and change variables in a controlled way. Setters can set limitations on the data that gets changed. And getters can alter the data that gets retrieved.

This is what our class looks like with get and set functions to control the leg count.

            class Animal {
  private _name: string;
  private _legs: number;
  private _color: string;
  private _decoration: string;

  constructor(name: string, legs: number, color: string, decoration: string) {
    this._name = name;
    this._legs = legs;
    this._color = color;
    this._decoration = decoration;
  }

  get legs() {
    return this._legs;
  }

  set legs(legCount: number) {
    if(legCount > 1 && legCount < 5) {
      this._legs = legCount;
    }
  }
}

    

Learn Encapsulation and Avoid Global Variables

Here's the final code. Recap what you've learned to ensure your understanding:

  • Add getters and setters for the rest of the variables.
  • Return the animal name as a span tag: <span>llama</span>
  • Change the decoration variable to allow for multiple decorations. Create an appropriate getter and setter to reflect that change.

If you want to keep your code running like a well-oiled machine, you absolutely need to use encapsulation. Avoid global variables at all costs. And if you do need to share variables between objects, you can view the TypeScript documentation on how to create class/static variables to learn how.