Object-oriented programming in JavaScript #4. Encapsulation.

Viktor Kukurba
6 min readApr 7, 2019

--

In this story, we will talk about Encapsulation principle in OOP and its implementation in JavaScript ES7.
When talking about encapsulation, private class members are often mentioned, and that is correct, but it covers just a part of this principle.
More advanced and high-level explanations are associated with meaning Encapsulation as a concept of bundling data related variables and properties with behavioral methods in one class or code unit.
But if we are still talking about privacy, we can give another definition.
Encapsulation is an approach for restricting direct access to some of the data structure elements (fields, properties, methods, etc).

Let’s look at an example.

We have a real-life component - car. Usage of the abstraction principle defines the context of data that will be needed in our application. In this case, we will need a car properties model name, current speed, max speed, and boolean engine prop that will be responsible for a state if a car is turned on or off.

Encapsulation principle means that we should add to the same class behavioral methods (drive, stop, etc.). Those may be used in our application and also to provide restricted access to changes in class instance’s state. We don’t want a client of our class to be able to turn off the car and then still be able to set its speed value to 100 miles per hour.

In more complex applications you may need more properties that describe the other car subsystems like lights or wheel.

In that case, you will have to make few abstractions for those subsystems that will encapsulate its state and behavior, and then you’ll be able to compose car Class with all that functionality.

Video tutorial about encapsulation.

Let’s take a look at the implementation of Car class described above that supports the encapsulation principle.

Here we have a simple constructor method that initializes a car instance properties. The method drive is asynchronous, it receives a parameter that is the desired speed, turns the engine on, and sets speed asynchronously to needed value, logs and returns it. The method stop is the opposite to drive, it asynchronously sets the speed to 0 and turns off the engine.

Method setSpeed looks a little bit complex. The main idea is to be able to set speed not immediately, as any car needs more or less time to accelerate to requested speed. Here we’ve used Promise to achieve it. First of all, we would like to check the state of the car inside the executor function (is engine on and compare requested speed to speedLimit) whether we are able to set that speed in current condition. Of course, if conditions are incorrect, we reject to set the speed.
Speed (this.speed) increasing/decreasing is implemented inside the setInterval function if program reaches desired value of speed it resolves Promise.
Also, we’ve overridden methodtoString here to display the state of the car.
Finally, we’ve created an instance of class Car and function testDrive to verify expected behavior.

The idea of encapsulation in the current example is to create a code unit class Car that has both data representation of car state and methods to work with that state, and of course, to provide a safe interface to work with that state.

But what about restricting direct access to properties like engine and speed? We will look into it in the further part of the article.

User of Car class can easily change engine and speed state without using setSpeed, drive and stop methods. That can cause a code “crash”.

To prevent such cases many programming languages (like C#, Java) provide private accessory modifier. At this point in JavaScript private fields are in proposal stage. But you already can use it if are familiar with babel compiler it has @babel/preset-stage-3 and plugin @babel/plugin-proposal-class-properties. It provides class properties that also includes private one, marked with a symbol at the beginning of the declaration(#nameOfPrivateProp).

Here is babel configuration implementation with private properties.

Of course, you’ll have to install babel and plugins. To do it you can use npm or other options that are described in details here.
In this example#speed and #engine are private, and you are NOT able to access it on Car class instance like car.#speed .

It makes sense to set the other two properties private as well, but for the purpose of demonstration, one property is enough.

Let’s also consider cases when we are not able to use babel. But even if you are using it, these approaches still will be interesting and useful for you. So, let’s take a look at what we can do here.

One of the most popular solutions is simply a convention. It means that the team decides how to mark private fields. For example underscore(_) is often used for that purpose. There are two options here: to add it in the beginning or at the end of the variable declaration. Before starting development, engineers should decide what convention to use and indicate it somewhere in the development documentation.

We will have something like this in our case

constructor(model, speedLimit = 100) {
this.model = model;
this._engine = TURN.OFF;
this._speed = 0;
this.speedlimit = speedLimit;
}

Based on our convention, this._engine & this._speed are private. But you still are able to access its values and change them like car._speed; or car._engine = false;. Anyway, you and everybody in your team will understand that there is something wrong with that and will address that issue.

Now, take a look at the next option that really gives behavior of the private field, and user of the class instance won’t be able to access it.

If you were using JavaScript before keyword class was introduced, you should be familiar with constructor functions that often were used to create a type. For example:

function Car(name) {
var privateProp = 'private value';
this.name = name;
}
var car = new Car(name);

By the way, keyword class is just syntactic sugar for that kind of constructor function. You still can use such approach if you wish. So, as you can understand, those variables inside the function Car that are not attached to this are private and not accessible outside the class car.privateProp === undefined .

Described approach can be also applied using class. In that case, we will have to define private variables and code that uses those variables in constructor method.

constructor(name) {
this.name = name;
var speed = TURN.OFF;
this.drive = (s) => {
speed = s;
}
}

The main disadvantage is that we have to create a method that uses private variables for each instance, and it makes class methodconstructor too complicated.

And in the last workaround, we will use WeakMap to store private properties values in the scope of Immediately Invoked Function Expression (IIFE).

We will create WeakMap variables for each private property in IIFE and will use class instance(this) as a key to get or set value for each instance, which means that WeakMap will store values for each instance.

Let's rewrite our previous implementation with WeakMap.

For such implementation WeakMap is used but not a Map, because of how the objects that are used as keys are treated during garbage collection. While an object is a key in Map, it won’t be garbage collected, but in case of WeakMap, it will be collected after removing all other references. If you haven’t worked with WeakMap and Map in JavaScript, please try to found more information about it. For example, this short explanation post works well for me to understand the main difference and reasons for using these new EcmaScript6 options for various solutions.

As you can see, encapsulation principle finds various implementation solutions in JavaScript, and it looks like it will have the most efficient and clearest one available soon. But we can already easily simulate its behavior.
In my experience, I can say that convention is used in most cases. And it makes sense when developers understand that accessing prefixed properties is forbidden, but sometimes you can find programming code where developers ignore its restriction. If I’m already using babel on my project and need privacy, I’ll go for plugin-proposal-class-properties, as its implementation will be used in JavaScript engines in the future anyway. And If I’m not using babel on my project I won’t add it just for that feature.
If you prefer strong typing programming languages, you can use TypeScript as it provides public, private and protected access modifiers, which gives you efficient abilities to implement encapsulation in the best way.
So, here we have various options and each of them can be preferred in a specific case. I think that it is good to be armed with that variety of solutions to archive the most efficient, effective and full of sense implementation in solving different problems.

--

--