DEV Community

Cover image for Scope in JavaScript: Understanding Lexical Environment and Scope Chain
Dheeraj Menon
Dheeraj Menon

Posted on

Scope in JavaScript: Understanding Lexical Environment and Scope Chain

The concept of Scope in JavaScript is directly related to Lexical Environment. Understanding the Lexical Environment is thus very helpful to learn about Scope and have the prerequisite knowledge to dive into Closures.

Scope is the region in which a particular variable can be accessed in the code. It determines the accessibility of a variable. So, how is the scope of a variable decided? This is where Lexical Environment comes into play.

Lexical Environment

In JavaScript, every code block has an internal associated object known as the Lexical Environment. It consists of 2 parts:

  1. Environment Record - An object that stores all local variables as its properties.
  2. A reference to the outer lexical environment.

In short, the lexical environment of any code block is its local memory and the lexical environment of its parent.

Let's understand this through an example. Consider the following code.

Code

Here, we assigned the value 10 to a variable inside the main function. When we try to access this value from a function inside the main function, we successfully obtain 10. But when we try to access it outside the main function, an "Uncaught ReferenceError" is thrown.

Code Result

Why does this happen? We need to understand the mechanism of the lexical environment to know why.

Lexical Environment Mechanism

When we run this program, a Global Execution Context is created and it is pushed onto the Call Stack. The main function is now invoked and an execution context is created which is also pushed onto the call stack. Since the inner function is invoked inside the main function, another execution context is created and pushed onto the call stack.

Call Stack

Whenever an execution context is created, a lexical environment is also created. For the above 3 execution contexts, there are also 3 lexical environments.

Because of lexical environment, alongside the environment record, we are able to get a reference to the parent lexical environment. Since inner function is lexically inside main function, it will have access to the lexical environment of the main function. Similarly, the main function will have access to the lexical environment of the global execution context. The lexical environment of the global execution context has a reference that points to null.

Call Stack 2

Now, let us see how the lexical environment is used in this program. When JavaScript engine encounters the console.log(x) inside the inner function, it tries to find out the value of x inside the local memory of inner function.

Since the variable does not exist inside the inner function, it uses the reference and goes into the lexical environment of its parent which is the main function. It searches for the value of x and finds it. Hence, we are able to see 10 printed on the console as our first result.

As for our console.log(x) outside the main function, the JavaScript engine tries to find the value of x in the global execution context. Since it is not found, the engine then uses the reference to parent lexical environment which points to null. Hence, we get an error in this case.

Lexical Environment in the browser

It's time to observe the Call Stack and Scope in the browser for the above example. Note that Lexical Environment is a specification object and only exists theoretically in JavaScript to describe how things work. But the Scope will do a fine job in helping us understand the lexical environment for each execution context in the call stack because it will show us the local memory for each lexical environment and the references to all outer lexical environments.

Note: (anonymous) refers to the global execution context.

Global Execution Context

The Global Execution Context is added to the bottom of the call stack when we run a program. Its scope is global i.e any variable present inside it can be accessed anywhere in the program. It has the window object as well.

GEC

Execution Context of main function

The Lexical Environment of our main function is the local memory of main function along with the lexical environment of global execution context. In the local memory, we have the definition of inner function and the value of x.

Main

Execution Context of inner function

The Lexical Environment of our inner function is the local memory of inner function along with the lexical environment of main function and its parent lexical environment as well. The lexical environment of our main function is called a "Closure". A closure remembers its outer variables and can access them. In our example, we can see that the inner function is enclosed inside the main function. Because of this, the inner function is able to access the variable in the main function.

Inner

Scope Chain

The mechanism of finding variables shown above in which our code started searching for the variable in its lexical environment and continued its search using the reference to outer lexical environment is known as the Scope Chain.

If the variable is not found, the JavaScript Engine will look into the outer scope until the variable is found or the global scope is reached. If the variable is not even found in the global scope, we use its reference to the null value and a reference error is thrown.

References

  1. Namaste JavaScript

  2. javascript.info

Top comments (0)