Encapsulation in JavaScript

Video Tutorial
FREE
Encapsulation in JavaScript thumbnail
This video belongs to
JavaScript Course With Certification: Unlocking the Power of JavaScript
9 modules
Certificate
Topics Covered

Overview

Encapsulation can be defined as “the packing of data and functions into one component”. Packing, which is also known as bundling, grouping and binding, simply means to bring together data and the methods which operate on data. The component can be a function, a class or an object.

Packing enables “controlling access to that component”. When we have the data and related methods in a single unit, we can control how is it accessed outside the unit. This process is called Encapsulation.

What is Encapsulation in JavaScript?

You might already be familiar with systems like a vending machine, a bank locker, or even a purse. All of these contain some items in them. Items that are contained within a system are made available to use through the system.

For example, you can not directly access a snack from a vending machine without having to use the machine. The vending machine has a few defined procedures which allow you to access the items in it. This is exactly what is meant by Encapsulation.

In the world of programming, encapsulation is the bundling of data with the methods that operate on that data.

Working of Encapsulation in JavaScript

Consider the following object “student”. It has three attributes id, name, and marks.

In the present scheme of things, you can access these attributes from outside the object.

You can also change the value in a similar fashion.

The above code prints {id: 12, name: 'Isaac', marks: 91} to the console. Everything looks fine until we try to run the following code.

We get {id: 12, name: 'Isaac', marks: "Ninty One"} on the console. We don’t want marks to store a string value.

How can we control the access to the attributes of this object?

Encapsulation may be the answer here. With Encapsulation, we can ensure that we give direct access to data to the associated methods in the object and further give access to these methods to the outer world.

But does this mean that the outer world has no direct access to internal data? No, it does not.

The above code prints {id: 12, name: 'Isaac', marks: "Ninty One"} on the console. We were able to write a function that puts a check while setting the value for marks, but how do we ensure that the outer world has no direct access to data?

Ways to Achieve Encapsulation in Javascript

Trying for Encapsulation in JavaScript Using the Functional Scope

Since a variable inside a functional scope can not be accessed from outside, we can restrict the direct access to data using the functional scope. Let us understand with an example.

In the above example, we begin with writing a functioning student and adding all data (id, name, marks) and methods (setMarks) inside it. The idea is when this function is used in place of a plain object, we will be able to restrict the direct access to those contained in it. Using this, variables id, name, and marks are not accessible from the outer scope. But this also means that we will not be able to access the inner getter and setter methods. This looks like a half baked solution.

Let us now explore how can we achieve Encapsulation by enhancing this concept.

Achieving Encapsulation Using Closures

A closure is the combination of a function bundled together with references to its surrounding state. This means a function is able to remember the variables from the scope it was defined in, even when it now lives outside that scope. Let’s understand with an example

In the above example, "points" is a local variable inside the scope of the function "startGame". The inner function "showPoints" is the closure that uses the variable declared in the outer function "startGame".

Another example of understanding closures could be when the inner function is returned from the outer function, and by virtue of closure, it is able to access the variables defined in the outer function at the time of the call.

Here, the inner function square has access to “data” even when "square" is called outside the scope of the outer function squareArray.

Let us now understand how can closures help in Encapsulation. The first thing to be achieved here is to restrict access to inner data. Since data inside a function scope is not accessible outside the function, we initialise the object as a function and declare the variables id, name and marks inside it.

Now that we have restricted the access, we would want to use the methods which operate on the data.

We created an object inside the function, added a setMarks method and returned the object. This means whenever the function is called, the object containing the method will be returned, and thus the setMarks method will be accessible to the outer world while protecting the data we do not want to share. You should note that the function is immediately invoked.

If you check the type of “student”, it will be an object rather than a function. This is because the function was immediately invoked, and it returned an object whose reference is stored in the student variable.

Continuing on the earlier example, when we try to log “student” on the console, we just get an object containing the setMarks method.

How do we access other data?

While we do not want the outer environment to edit the inner data, we still want it to be able to access it. This is where we will introduce getters. This means all operations which are needed on data will have to be in the form of the methods which will be returned within the object “obj”.

Let us see an example.

We now see the expected behaviour. We are able to control access to the data inside a component.

Encapsulation Using Private Variables

Classes were introduced in ES6. Using classes, Encapsulation can be achieved in a more standard way. We begin with defining the “Student” class. Like all major programming languages, we can think of a class as a blueprint here. You can learn more here

Let us understand with an example.

This appears to be a standard solution, but if you examine it carefully, you are able to access the properties of this object directly.

This is because, unlike most other languages, data hiding is not inherent to the classes in javascript. This means that by default, properties can be accessed and modified from the outer world.

You should note that properties declared within an object/class are not the same as variables in javascript. You can see this difference in the way object properties are defined (without any var/let/const keyword)

If we start using variables instead of properties, we might just be able to achieve Encapsulation. Since variables are lexically scoped, they are not accessible from outside the scope they are defined in.

Let us look at the following example to learn more.

Here, we declare the properties within the scope of the constructor instead of defining them as properties at the object level. We then define the constructor and initialise the object properties as variables within the scope of the constructor.

ES6 also introduced the “get” keyword, which makes using getters easy. When you initialise the object of the student class in the following example, you are able to access the property using {object instance}.name

Advantages of Encapsulation in JavaScript

Protection of Object against Illegal Access:

As we established in the article, encapsulation guarantees that we are in control of what is accessible from outside the object.

Decoupling Implementation Details

Since Encapsulation enables operating on data using a public interface, it is easier to update the implementation because the tight coupling is eliminated. This is an excellent way to always code against an interface. This also means that with Encapsulation, the object is scalable to accommodate future changes without breaking compatibility.

The Coupling means the extent to which two modules are dependent on each other. Decoupling refers to eliminating such dependencies.

Shared Mutable State

It also eliminates more than one party operating on the same data at the same time, which might create a race condition. Imagine an object without Encapsulation being updated by two asynchronous functions.

consider this example,

In the above example, since we have used Encapsulation, the instances first and second are different from each other, and on performing two different operations, we get the expected results. Note that since the instance is separate, there is no chance for a race condition.

Conclusion

  • Encapsulation brings together data and the methods which operate on data in a single unit.
  • It allows controlling access to the unit.
  • Encapsulation in javascript is achieved using closures or using private fields.
  • Using Encapsulation enables the decoupling of implementation details from the interface.