Lexical Environment and Lexical Scope ?

Lexical Environment and Lexical Scope ?

Before beginning with lexical environment . you have to knowledge about how execution context works and how scope works in javascript .you can read from here

Execution Context
Scope

let's start with the what is meaning of lexical

Lexical: The term "lexical" refers to the way in which programming languages determine the scope of variables and functions based on their location or position in the source code.

Lexical Environment

A lexical environment is the set of variables and functions that are in scope at any given point in the code. Every time the when JavaScript engine creates an execution context to execute the function or global code, it also creates a new lexical environment to store the variables defined in that function during the execution of that function. It is created by the combination of the following two things:

  1. The current scope's environment

  2. A reference to the outer scope's environment (also known as the scope chain)

scope refers to the visibility or accessibility of variables, functions, and objects in a particular part of the code.ach block of code creates scope and a lexical environment.

javascript looks at a lexical environment when you ask for a variable while running a line of code inside any particular execution context if it can’t find that variable in its block scope it will go at the outer reference or block and look for the variables And that outer reference is where the function sits lexically is its outer environment.

sometimes when the same variable or a new copy like is called the same function twice then each gets its execution context and though it looks like the same variable, but actually it is two different variables in memory.

Lexical Environment: Local Memory + Lexical Environment of its Parent

So in short, a lexical environment is a place where variables and functions live or physically present during the program execution.

For example:

function two(){
  var a;
  console.log(a);
}
function one(){
  var a=2;
  console.log(a);
  two();
}
var a=1;
console.log(a);
one();
//output
//1 2 undefined

When the program runs, the global execution context is created first, and a is assigned a value of 1 in the global scope.

Next, the one() function is called. When one() is called, a function execution context is created for one(). In the function execution context of one(), a new variable a is created and assigned the value of 2.

After that, one() calls the two() function. A new function execution context is created for two(). In the execution context of two(), a new variable a is created but is not assigned any value yet.

When console.log(a) is called inside the two() function, JavaScript first looks for the value of a inside the function's own execution context. Since a has not been assigned a value yet in the execution context of two(), undefined is printed to the console.

Finally, control returns to the execution context of one(), and the function completes. After that, control returns to the global execution context and the program terminates.

In terms of lexical environment and scope, each function creates its own execution context with its own lexical environment. The lexical environment of a function consists of all the variables that are in scope at the time the function is defined. In this case, the two() function has access to its own variables, as well as the variables in the execution context of one() and the global execution context. However, one() and the global execution context do not have access to the variables inside two() since they are not defined in their lexical environments.

Let's take another example:

function two(){
  console.log(a);
}
function one(){
  var a=2;
  console.log(a);
  two();
}
var a=1;
console.log(a);
one();
//output
//1 2 1
  1. When the program runs, the global execution context is created first

  2. The global variable a is declared and assigned a value of 1 in the global scope.

  3. When one() is called, a function execution context is created for one(). Inside the one() function, a new local variable a is declared and assigned a value of 2 in its own scope. This local variable a shadows the global variable a within the scope of the one() function.

  4. Inside the one() function, console.log(a) is called, which outputs the value of the local variable a, which is 2.

  5. The one() function then calls the two() function. A new function execution context is created for two(). Inside the two() function, the code attempts to access the variable a. Since there is no local variable a declared within the two() function, it looks up the scope chain to find the variable a in the parent scope, which is the global scope. Therefore, it outputs the value of the global variable a, which is 1.

  6. Finally, control returns to the global scope, and console.log(a) is called again. It outputs the value of the global variable a, which is 1.

Lexical Scope

let's understand lexical scope and scope chain with some other examples

function a() {
  let a = "a";
  function b() {
    let b = "b";
    function c() {
      let c = "c";
      console.log(b);
    }
    c();
  }
  b();
}
a()

lexical scope refers to the ability of a function scope to access variables from the parent scope. When there is lexical scope, the innermost, inner and outermost functions may access all variables from their parent scopes all the way up to the global scope.

  1. When the program runs, the global execution context is created first. but here when no global variable is declared thus the default window object, this and function declaration are assigned only

  2. The when a() function is called. a function execution context is created on the top of global execution context .Inside the a() function, a local variable a is declared and assigned the value "a".

  3. The b() function is defined inside the a() function. then another a function execution context is created on the top of a() function execution context . Inside the b() function, a local variable b is declared and assigned the value "b".

  4. The c() function is defined inside the b() function. then another a function execution context is created on the top of b() function execution context . Inside the b() function, Inside the c() function, a local variable c is declared and assigned the value "c".

  5. Inside the c() function, console.log(b) is called. JavaScript first looks for the variable b within the local scope of the c() function. Since there is no variable b declared within the c() function, it looks up to parent scope. which is the b() function. where in b() we found the variable b value . this is due to ability of a function scope to access variables from the parent scope is lexical scope.

let's take another example with some modification

let d="global";
function a() {
  let a = "a";
  function b() {
    let b = "b";
    function c() {
      let c = "c";
      console.log(d);
    }
    c();
  }
  b();
}
a()

The scope chain is the mechanism by which JavaScript determines which variables are accessible in a particular portion of the code. When a variable is accessed in a particular portion of the code, JavaScript looks for the variable in the current scope. If the variable is not found in the current scope, JavaScript looks for the variable in the outer scope, and this process continues until the variable is found or the global scope is reached.

  1. When the program runs, the global execution context is created first. but here variable d is declared in the global scope and assigned the value "global".thus the default window object, this and function declaration are assigned .

  2. The when a() function is called. a function execution context is created on the top of global execution context.Inside the a() function, a local variable a is declared and assigned the value "a".

  3. The b() function is defined inside the a() function. then another a function execution context is created on the top of a() function execution context . Inside the b() function, a local variable b is declared and assigned the value "b".

  4. The c() function is defined inside the b() function. then another a function execution context is created on the top of b() function execution context . Inside the b() function, Inside the c() function, a local variable c is declared and assigned the value "c".

  5. Inside the c() function, console.log(d) is called. JavaScript first looks for the variable d within the local scope of the c() function. Since there is no variable d declared within the c() function, it looks up to parent scope which is the b() function. where in b() we not found the variable value of b . first looks at the lexical scope . if we do not find in lexical environment then it looks up the scope chain to find the variable d in the parent scope, which is the b() function. . If d is not found in the b() function, it looks up the scope chain again to find the variable d in the parent scope, which is the a() function. If d is still not found, it continues to look up the scope chain until it reaches the global scope. In this case, the variable d is found in the global scope, and its value is "global". Therefore, it outputs "global".