Encapsulation using closures in JavaScript

One of the unusual features of JavaScript is encapsulation using closures. It is unusual because in many other programming languages, this mechanism is implemented using classes. In JavaScript, however, there is a different mechanism that may seem strange if you don't understand the nuances of variables and functions.

If a variable or object in JavaScript is not placed inside any function, they become global. All global elements in JavaScript are properties of the global object. For browsers, this object is window. At the same time, statements like for, if, and others do not affect the visibility of variables.

Creating global variables is generally undesirable as it can lead to hard-to-detect errors and makes it difficult to transfer code to other applications.

For example, if you write a library with the following content in foo.js:

var a = "Hello";

function Hello2() { 
    alert(a); 
}

Hello2(); 


And then include it in your project:

<script>
    var a = "Goodbye"; 
</script>

<script src="foo.js"></script> 

<script> 
    alert(a); 
</script> 

The variable from the library will overwrite the initial value of the variable, and as a result, the last alert will display "Hello" instead of "Goodbye". It is common practice to minimize the use of global variables by hiding (encapsulating) all data within classes.

Encapsulation is the process of placing all data and functions related to the same concept into a single entity. Unnecessary data for the user is hidden inside the entity and is only accessible within it.
In object-oriented languages, encapsulation is achieved through the use of access modifiers. In JavaScript, closures are used to achieve encapsulation.
A closure is a function plus all the external variables that it has access to.
Access to variables in functions is done in the following way. All variables inside a function in JavaScript are properties of the LexicalEnvironment object, also known as the lexical environment. This object is internal and cannot be accessed directly.

In addition, the function has a special internal property called [[scope]], which stores the external variables for that function. The function first looks for variables inside the LexicalEnvironment and if it doesn't find them, it looks in [[scope]]. Thus, the function first searches for the necessary elements within itself and only then refers to external elements. It also takes the current value of the variable from the external scope. Furthermore, in JavaScript, you can declare other functions inside functions.

A vivid example of closures is a counter code:

function useCounter() {
    var Count = 1;

    return function() {
        return Count++;
    }; 
} 

var counter1 = useCounter();
alert(counter1()); // 1
alert(counter1()); // 2
alert(counter1()); // 3

var counter3 = useCounter();
alert(counter3()); // 1

Thanks to closures, the inner function has access to the Count variable and can return its value to the external code, while users of the useCounter function do not have direct access to the Count variable, as it is not visible to them. By using closures, the Count variable is hidden inside the useCounter function.

Similarly, you can create an object that hides a variable using closures:

function Count2() {
    var count = 1;

    return { 
        getValue: function() {
            return count++;
        }, 

        setValue: function(value) {
            count = value;
        }, 

        reset: function() {
            count = 1;
        } 
    }; 
} 

var counter33 = Count2();

alert(counter33.getValue()); // 1
alert(counter33.getValue()); // 2

counter33.setValue(7); 
alert(counter33.getValue()); // 7

Thus, closures can also be used inside objects.

To implement modules, you can use an immediately invoked function:

(function() {
    var msg = "hi"; 


    function showmsg () { 
        alert(msg); 
    } 

    // display message 
    showmsg(); 
})(); 

In this code, the msg variable is accessible inside the showmsg function thanks to closures, and by using an immediately invoked function, the variables do not enter the global context.