JavaScript execution context — lexical environment and block scope (part 3)
JavaScript has three types of scopes since ES6 update:
- Global scope
- Function scope
- Block scope
What is a scope from the execution context point of view?
The global scope is the global execution context, and the function scope is related to the function execution context.
The block scope, introduced in ES6, is different from its two siblings.
An example of the global scope
The easiest way to understand the block scope is by comparing it with the other two scopes.
A variable works similarly in a global and function scope, so I will only talk about the global scope and the block scope in this post.
In this case, we have only one global execution context and one global variable environment.
The second apple
assignment overrides the previous one. At the end of the execution, there is only one apple
variable holding a value “banana”.
From the scope perspective, we can say that the apple
variable is in the global scope.
Block scope
We can rewrite the example above with a let
to introduce a new scope, the block scope.
The console logs two different values. The first apple
variable holds a value “apple”, while the one in the if
block has a value “banana”,
How come we have two variables with the same name?
Lexical environment
Let’s go through the two-step process and demystify how it works.
At the compiling step, the undefined apple
variable is added into the global execution context.
At this moment, the JavaScript engine decides to skip the second apple
for two reasons:
- it is a variable created with the
let
, and - it is in a block scope.
Next, the execution step starts. The first apple
variable is assigned with the value “apple”.
When reading the if
block codes, a nested compiling step happens. The second undefined apple
variable is created.
Instead of creating it in the variable environment, this apple
variable is added into the lexical environment.
Soon, a value “banana” is assigned to the apple
in the lexical environment.
Now we have two variables with the same names managed in two environments. That’s how JavaScript engines deal with the let
, and is still backward compatible with the var
.
Scope stack in the lexical environment
To better understand the difference between let
and var
, let’s combine them together in a fun example.
At the compiling step, undefined apple
and grape
variables are initialized in the variable environment. The grape
initialization is hoisted. Meanwhile, a banana
variable is created in the lexical environment.
The execution starts. apple
is assigned with a value “global apple”, while banana
gets “global banana”.
It is time to take care of the variables in the block.
Hey, it is another banana
variable. Can we have two banana
variables in the same lexical environment?
When seeing let
and const
variables in a block scope, the JavaScript creates a separate area for them. The lexical environment maintains a stack-like structure for its variables, so variables with the same name don’t conflict with each other.
Here, the undefined banana
and orange
variables sit in a stand-along scope.
Soon, both are assigned with a value correspondingly.
After executing the last assignment, all the variables are ready.
When logging the first variable, the JavaScript engine first tries to find the apple
in the lexical environment from top to bottom. Then it exams the global variable environment and finds the apple
, logging out “global apple”.
When searching for the banana
, the JavaScript engine follows the same steps, finds it, and logs “block banana”.
At this moment, no more executable codes remained in the block. The block scope is removed.
The script continues execution. It finds the banana
and the grape
in the global variable environment and logs out “global banana” and “global grape” correspondingly.
When the script searches for the orange
, the variable doesn’t exist anywhere, because the scope whereorange
existed was removed. It throws an error “orange is not defined.”
The entire script ends.
Other than the scopes, is there any other gotcha related to let
and const
?
Tricks of creation, initialization, and assignment
From the compiling to the execution, a variable goes through three steps:
- Creation
- Initialization
- Assignment
What will the console log? Is it apple
? Alternatively, is it banana
?
Surprisingly, an error displays “Cannot access apple
before initialization.”
This error is related to the hoisting.
- For a
let
variable, its creation is hoisted, but not initialization and assignment. - For a
var
variable, its creation and initialization are hoisted, but not the assignment. - For a
function
, its creation, initialization, and assignment are hoisted at the same time.
People named the code area before the variable initialization, the temporal dead zone.
- If you try to access a variable before the creation, you see the error “[variable name] is not defined.”
- If you decide to access a variable before the initialization, you see the error “Cannot access [variable name] before initialization.”
- If you log a variable before the assignment, you see the value
undefined
.
Let’s everything you need to know about the scope and lexical environment.
What are the takeaways?
- The lexical environment is another component of an execution environment.
let
andconst
variables in a block scope are created at the execution step instead of the compiling.- These variables are stored in the lexical environment.
- Multiple block scopes are maintained as a stack structure in the lexical environment.
- When the JavaScript engine executes all codes in a block scope, the related
let
andconst
variables are removed.
Join Medium
Purchasing Medium Membership through the above link means that I can get income through the referral link. This does not mean that you have to buy from the link, nor does I deny or oppose other channels. It is your right to know.
Up next…
Further reading
- If you want to know a different understanding of the lexical environment, this post worth reading.
- If you are not familiar with hoisting, I hope my post could help.
- This answer is a comprehensive one explaining the temporal dead zone.