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:

  1. Creation
  2. Initialization
  3. Assignment

What will the console log? Is it apple? Alternatively, is it banana?

Surprisingly, an error displays “Cannot access applebefore 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 and const 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 and const 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.

a coder 🧑🏻‍💻