Scope

Definition

js this

The term “scope” (variable definition area) is related to the term variable and represents a part of the program code in which a variable is available. The basic characteristic of the definition area is the border of that area. The variable can be accessed only if it is requested within its area of ​​definition, i.e. the variable cannot be accessed if we are outside the scope.

It should be noted that “scope” is not the same as “context”. These two terms are often confused because they have some similarities, but you should know that the term “scope” is a property of a variable, while “context” is a property of program code. During execution, the program passes (enters and exits) through different areas of variable definition (scope), and the variables are then either in “context” or not. “Contex” is similar to the area of ​​definition because it also depends on the position in the program (“lexical context”) or the time of code execution (“execution context”).

Division according to time of definition

Static (“lexical”) scope

The lexical scope is defined at compile time, so that the function then remembers the references pointed to by variables in the lexical scope chain (called “EARLY BINDING”)

“Lexical scope” directly depends on the place where the variable is declared in the code itself, therefore it can be concluded that this type of variable domain is defined at the time of writing the code. Accordingly, this code cannot be changed over time (except by changing the code itself), so it is also called “static scope”.
The only way to “cheat” the lexical scope is to use the eval command, but it is recommended to use this command as little as possible (some serious programmers even call it “evil”). This type of scope is used in most programming languages including JavaScript.

Dynamic scope

Dynamic scope is defined at runtime, so it binds to the references pointed to by variables at that moment of code execution (so-called “LATE BINDING”)

In programming languages that use dynamic scope, after searching for a variable within the parent function, the search for a variable in the function that called the parent function starts. Dynamic scoping is very difficult to monitor, which is why it is less common in programming languages. Used in Lisp, Perl…

Division according to the size of the definition area

Local scope

Until the appearance of ES6 in JavaScript, it was possible to create a local scope only with the help of a function. This was considered one of the biggest flaws of the language itself. This has been fixed with the new language revision “ES6” using the “let” reserved word to define the scope block.

a) Function scope

When a variable is declared inside a function it defines a local scope which is also called “function scope“.

Local variables (variables inside functions) and function scope exist only when calling a function (invoke function). This is why the last command from the previous example throws an error. Viewed from the global scope, if we ignore the scope boundaries, the variable “internal” does notexists, because the function “a long time ago” was executed, i.e. the variable existed at the time of execution but no longer exists afterwards.

b) Block scope

EcmaScript 5 does not support “block scope” (an area defined by a block of code) but with ES 6 a new special word “let” was introduced which declares a variable inside a code block and thus defines a local block scope. One of the important features of creating a block scope with the keyword let is that it does not execute hoisting! Therefore, it is necessary to place variable declarations at the beginning of the block.

Most often, a block of code is defined with curly brackets as in the following example:

The special word “let” can also be found outside the curly braces and define them as a defined area if it is used as part of a for loop.

In addition to declaring a local variable within the curly braces, the “let” reserved word re-declares a new variable “i” in each iteration of the loop. This fixed an ES5-specific problem where closure returns only the last “i” if i is declared with “var”, which is shown in the following example:

NOTE:
It should be mentioned that in ES5, in addition to function scope, it is possible to create a new scope block with a part of the code that handles errors in the code and is defined with the special word “catch”:

This way of creating a local definition area is used by transpilers to create a scope block in ES5.

Global scope

According to the ES5 standard, any variable that is not defined with the reserved word var within the function belongs to the “global scope”, which means that the variable is available to all parts of the program. Within the browser, the global scope is the window object, so all global variables become properties of the “window” object. The “Lifetime” of a global variable lasts until the page is closed or reloaded ie. window object. If the variable is not declared with the reserved word var the variable will become global even if it is defined (assigned a value) inside the function.

Instead, the global variable will be created by error, so it is recommended to use the strict mode “use strict”, which reports an error when creating the global variable.

Characteristic examples

Nested functions

When we look at a variable inside a function (at the time of writing the code) we know that it is available throughout the function. If there are other functions within the function, they will be available there as well. Which means that every nested function has access to an external part of the code (so-called parent scope), i.e. any variable, function or object.
With languages that support lexical scope, the compiler first searches for a variable inside the function itself that is started, then searches in a function that is outside and contains the function,…and so on until the global domain where it also searches for the variable, only if it is not found there then it returns that the requested variable is “undefined”.

Function call does not generate scope

Note that a function call inside another function does not create a lexical scope. In the following example, the function call to f2() is inside the function f1(), but this does not allow the function f2() to access the variable “a” declared in the function f1(). This happens becausethe function call statement inside f1() does not make a “lexical scope”!

Shadowing (shadowing of the variable)

In the following example, IIFE does not find the variable “x” in its function scope, so the compiler looks for it in the parent domain, where it is found.

If X is also declared inside an IIFE, the compiler will not use the variable from the global domain. The compiler first searches the parent function and when it finds what it is looking for, it stops the search. This phenomenon is called “shadowing”.

The compiler concluded after searching the local domain that variable X was declared in it, variable X has no value at the time of execution of the line of code from console.log because it is assigned a value later. From the compiler’s point of view, the code looks like this: