JavaScript Environment, Lexical Scope and Closures
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.”
Inside themultiplier
function, there is a constant 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
andfor
.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 defined, so that initially a
is 0
, but after the call changer
, a
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 multiplier
returns the product of a
and b
. a
set inside, but b
not.
Trying to solve the multiplication a * b
, javascript searches for values a
and b
. It begins to search locally and goes outside, one area of view per step, until it finds what it needs or until it realizes that it is impossible to find.
Therefore, in this example, JavaScript starts with a search a
inside the local scope - inside the function multiplier
. It finds the value immediately and goes to b
. It is impossible to find a value b
in the local scope, so it goes to the outer area. Here it finds b
- this is 10. a * b
turns 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 b
not been found here, JavaScript would continue to search b
outside the function, layer by layer.
Note that a = 7
did not affect the calculation, a
was found inside, so the external a
did 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:
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, function returns a value.
When you call this function, like this:
It will always returns 0.
Ok, back to the closures. Consider the following code:
The function createPrinter
creates a constant name
and then a function with the name printName
. Both are local to the function createPrinter
, and are available only inside createPrinter
.
It itself has printName
no 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 createPrinter
returns 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 myPrinter
and set its value, which is returned createPrinter
. It returns a function, so now myPrinter
it is a function. Call her, and the screen will display "King".
There is a strange thing here: this constant name
was 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 it 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 createPrinter
is 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
- Everything you wanted to know about JavaScript scope
- Variables and scoping / Exploring JS
- Scope / Wikipedia
- Closure / Wikipedia
- Closures in JS / MDN (here the examples use ES5, the previous version of the language standard)