Register to get access to free programming courses with interactive exercises

Encapsulation JS: Introduction to Object Oriented Programming (OOP)

Of all the many OOP features, there is one basic feature that most programmers associate with OOP — encapsulation. Encapsulation is the binding of functions and data into a single structure, the internal state (data) of which is hidden from the outer world (this will be discussed later). The term "method" refers to such functions. We've seen them before, and as you've probably noticed, they're everywhere in JavaScript.

The difficult bit

Before start digging into encapsulation, we should make a small step aside to talk about terminology and confusion among developers. This is especially important given that many students already come in having read all sorts of literature. If it's still hard for you to understand what's written in the next paragraph, just ignore it and come back to it at the end of the course.

A large number of sources define encapsulation as hiding data from direct external access (usually with keywords like “private” and “protected”). Moreover, this is the definition they'll want to hear from you at the interview, but it's actually only partially correct. Although this is a common definition, it's worth distinguishing between bundling data with methods and hiding that data. There are languages, such as JavaScript and Python, that have data bundling but no data hiding. And if you introduce data hiding into these languages, the architecture of the programs written there won't change, but if you separate data and methods, you will have to rewrite almost all the code. It's more or less the same with languages that have data hiding. If you remove it, not much changes except that developers must be extra cautious when working with objects.

To summarize: encapsulation is both merging and hiding where the bundling has taken place. When it doesn't exist, it's just bundling. In this course, we'll separate the idea of encapsulation (meaning only the binding of data and functions) and data hiding so that we can discuss these features independently. Otherwise, we'd all be a little confused about what is meant when the term encapsulation is mentioned.

Why we need to hide data is explained in the lesson about invariants

End of difficult bit :)

// Method call

// Function call

We'll talk about how methods work internally in the next lesson. For now, let's look at the external features of methods.

Working with methods instead of functions leads to one unexpected effect – it makes it possible to implement autocomplete methods in editors. This reduces the mental load and makes programmers very happy. There is a hypothesis that this method feature is what makes OOP so popular (not confirmed, but not impossible).

In languages with an advanced module system, auto-completion is also present when working with normal functions. But in any case, you must first write the correct module name. Example from elixir: User.getName(user). On the other hand, there are languages with Unified Function Call syntax (e.g. Nim), where normal functions can be called as methods and use autocomplete.

There's another quite controversial feature. For many developers, code with methods looks "more natural". Data abstractions, in their opinion, can only be formed based on methods. Abstraction is impossible until data and functions are bundled in one place. This view results from a lack of experience. Generally speaking, developers that say this have never worked outside popular OOP languages, and in their language, abstractions on functions are unnatural and even impossible.

This is not the case, of course. Just take the JS: Abstractions with Data, course to see for yourself.Abstractions and real-world modeling do not exist purely in OOP. They existed before and will continue to exist after.

Try to imagine adding someone as a friend in OOP style. Who should add whom (person 1 -> person 2 or person 2 -> person 1) and how do we avoid recursion if they both add each other?

Now let's look at the third feature of methods, which is even more interesting. It really helps to make working with code easier and the code itself shorter. We don't need to import anything extra when working with objects, as we do with functions. Any function that has been passed an object can invoke its methods as it wants. We would have to import the relevant functions if we were working with functions. This feature does limit your ability to extend objects (more about this in future lessons).

// send is just a function, so it must be imported before it can be used
import send from 'mail';

const sendEmail = (user) => {
  send('Subject', user.getEmail()); // You don't need to import getEmail because it is a method

And what if there's no object, as in the example above? Language developers and library developers do things differently. In JavaScript, normal functions and methods get along fine together. Roughly the same thing happens in Python. Normal functions don't appear as natural in Ruby and PHP (in modern frameworks), but they can still be constructed. There is no way to build regular functions in Java. Every function is a method. As a result, in Java, objects are created for practically everything. This considerably bloats the program and makes simple tasks difficult. However, there are other languages. Elixir and Clojure don't have methods in the way we know them now, and, more significantly, we don't need them because the code is brief, straightforward, and flexible.

Static methods are used in Java to simulate normal functions. They allow you to work without creating objects.

The fourth feature we'll look at is chains. Remember this function call:

const brand = 'bmw';
// This method does not change the string, but rather returns a new one!
brand.toUpperCase(); // BMW

This method returns a new string that also has methods, which means they can be called. For example:

// Using variable because we intent to overwrite it
let brand = 'bmw';
brand = brand.toUpperCase(); // BMW
brand = brand.concat(' & Kia'); // BMW & Kia
brand = brand.replace('BMW', 'Opel') // Opel & Kia
console.log(brand); // => Opel & Kia

And now a little magic. What if you don't create intermediate variables, but instead call them all at once? Let's try it out:

const brand = 'bmw';
// Now it's better to make a new constant, instead of a variable
const newBrand = brand.toUpperCase().concat(' & Kia').replace('BMW', 'Opel');
console.log(newBrand); // => Opel & Kia

The code is now more compact and clearer in some cases. But don't get carried away, it's very easy to go too far. This code can always be broken into several lines:

const brand = 'bmw';
const newBrand = brand.toUpperCase()
                      .concat(' & Kia')
                      .replace('BMW', 'Opel');
console.log(newBrand); // => Opel & Kia

Even if a value of a different type is returned, such chains can still be constructed. In this case, you can apply methods of the appropriate type:

const brand = 'bmw';
const newBrand = brand.toUpperCase()
                      .split('') // ['B', 'M', 'W']
                      .reverse() // ['W', 'M', 'B']
                      .join(''); // WMB
console.log(newBrand); // => WMB

These chains have a special name: fluent interface

Like almost everything else in the modern understanding of OOP, chains aren't mutually exclusive things. Moreover, they do the same thing as pipelines. If you're familiar with the command line, you've probably seen this code more than once:

# | is called a pipe
# Pipeline is often compared to beads (functions) on a necklace or bracelet, through which a piece of string (data) is passed.
cat sample | grep -v a | sort -r

This chain of commands sequentially passes data from left to right, passing it through different handlers. The concept itself came from mathematics, long before programming. In many languages, pipeline is implemented as a language construct (or a macro in Lisp). It's such a successful concept that they're now trying to integrate it into lots of different languages. For example, it exists in F#, OCaml, Elixir, Elm, Julia, and Hack. Right now, pipeline is being considered for addition in JavaScript. Take a look at an example:

// It's not convenient to work with functions in this way
const result = exclaim(capitalize(doubleSay('hello')));
console.log(result) // => "Hello, hello!"

// This is a different story (but it takes some getting used to)
// |> is a pipe, it sends the data that comes from the left of the function to the right
const result = 'hello'
  |> doubleSay
  |> capitalize
  |> exclaim;

console.log(result) // => "Hello, hello!"

Hexlet Experts

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

About Hexlet learning process

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
hours of theory

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing:">Bookmate</span>
<span class="translation_missing" title="translation missing:">Healthsamurai</span>
<span class="translation_missing" title="translation missing:">Dualboot</span>
<span class="translation_missing" title="translation missing:">Abbyy</span>
Suggested learning programs
Development of front-end components for web applications
10 months
from scratch
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.