JavaScript Environment, Lexical Scope and Closures

Mahendra Choudhary
Mar 20 · 6 min read

Part I. Environment

Let’s talk about the environment. Our planet is huge, but we all share it. If you build a chemical plant, it would be nice to isolate it from the outside world so that what happens in it remains inside. You can say that in this building its environment, microclimate, isolated from the external environment.

Your program has a similar structure for similar reasons. What you create outside — outside functions, if statements, loops, and other blocks of code — is in an external, global environment.

Constant age, function multiplier and variable result- all in the external environment. These components have a "global scope". Scope means "area where components are available."

There multiplier is a constant inside the function x. Since it is inside a block of code, it is a local constant, not a global one. It is visible only inside this function, but not outside. It has a local scope.

The function multiplier has another component from the local scope - the argument num. It is not defined as clearly as constants or variables, but behaves almost like a local variable.

We do not have access to the x outside, as if it does not exist there:

console.log Called in the global environment, and x not specified in this global environment. Therefore, we get Reference Error.

We can set x globally:

Now there is a global one x and its value has been displayed, but the local one x inside the function multiplier is still visible only inside this function. These two x have nothing to do with each other, they are in different environments. They do not collapse into one, despite the fact that they have the same name.

Any code block surrounded by curly brackets turns into a local environment. Here is an example with a block if:

The same works for cycles while and for.

Ok, local is not visible outside. But the global is visible everywhere. Even inside something? Yes!

This global variable a has changed inside the function changer. Function generally perform something only when it is called, not when it is determined, so that initially a it 0, but after the call changer, a it becomes 1.

Although it is tempting to put everything in the global scope and forget about the complexities of separate environments — this is a terrible practice. Global variables make your code incredibly fragile. In that case, anything can break anything. Therefore, avoid global scope, keep things where they belong.

Part II. Lexical scope

Take a look at this program:

The function multiplierreturns the product aand b. aset inside, but bnot.

Trying to solve the multiplication a * b, javascript searches for values aand b. He begins to search locally and goes outside, one area of ​​view per step, until he finds what he needs or until he realizes that it is impossible to find.

Therefore, in this example, JavaScript starts with a search ainside the local scope - inside the function multiplier. He finds the value immediately and goes to b. It is impossible to find a value bin the local scope, so it goes to the outer area. Here he finds b- this is 10. a * bturns into 5 * 10, and then into 50.

This whole piece of code could be inside another function, and still inside another function. And if it had bnot been found here, JavaScript would continue to search boutside the function, layer by layer.

Note that a = 7did not affect the calculation, awas found inside, so the external adid not play a role.

This is called a lexical scope. The scope of any component is determined by the location of this component within the code. And nested blocks have access to their external scopes.

Part III. Closures

Most programming languages ​​have something like a scope or environment, and this mechanism allows closures to exist. A closure is just a fancy name for a function that remembers the external things used inside.

Before we continue, let’s remember how functions are created and used:

f - a rather useless function, it always returns 0. The whole set consists of two parts: a constant and the function itself.

It is important to remember that these two components are separate. The first is a constant named f. Its value could be a number or a string. But in this case, its value is a function.

We used the analogy in previous lessons: constants like sheets of paper — the name on one side, the value on the other. Consequently, f- a sheet of paper with fon one side and a description of the function being launched on the other.

When you call this function, like this:

… creating a new box based on the description on this sheet of paper.

Ok, back to the closures. Consider the following code:

The function createPrintercreates a constant nameand then a function with the name printName. Both are local to the function createPrinter, and are available only inside createPrinter.

It itself has printNameno local components, but it has access to the area of ​​visibility, where it is located, to the external area, where the constant is given name.

The function then createPrinterreturns the function printName. Remember, function definitions are descriptions of running functions, just pieces of information, like numbers or strings. Therefore, we can return the definition of a function as we return a number.

In the outer scope, we create a constant myPrinterand set its value, which is returned createPrinter. It returns a function, so now myPrinterit is a function. Call her, and the screen will display "King".

There is a strange thing here: this constant namewas created inside the function createPrinter. The function was called and executed. As we know, when a function finishes, it no longer exists. This magic box disappears with all its insides.

BUT he returned another function, and already she somehow remembered the constant name. Therefore, when we called myPrinter, it brought out "King" - the memorized value, despite the fact that the scope where it was set no longer exists.

The function that was returned from createPrinteris called a closure. A closure is a combination of a function and the environment where it was specified. The function "closed" in itself some information from the scope.

This may look like a javascript trick, but closures, when used wisely, can make code more pleasant, cleaner, and easier to read. And the very idea of ​​returning functions in the same way that numbers and strings can be returned provides more options and flexibility.

Optional reading