Lexical Environment and Scope Chain

Lexical Environment and Scope Chain

JavaScript is one of the most misunderstood programming languages, if not the most misunderstood. Despite this, it is still the most extensively used programming language ( StackOverflow Survey 2020 ). 67 percent of developers use javascript regularly, however, a large chunk of the developer is unclear of how it works. It is important to have a thorough understanding of javascript to write more efficient and quality codes. JavaScript has some unique aspects that appear to be confusing at first, but once you get them, they will give you the power to better understand the language.

In this series of articles, we will learn some unique JavaScript concepts. Lexical Environment, Execution Context, Scope Chain, Closure, Event Loop are some features if anyone knows them well he/she will understand JavaScript in a lot better and simpler way. We'll start with the Lexical Environment because it's the first concept we need to understand. To understand the lexical environment we need to first understand the scope in javascript.

Scope

In JavaScript, Scope is a context for a specific section of code. Scope defines the accessibility of variables, functions, or objects of that specific section of the code. There are three types of scope in JavaScript: global scope, functional scope, and block scope.

scope1.png

JavaScript script starts with the global scope and all the functions in the script have their own function scope. There is also a block scope, which is typically seen in the script as an if-block or try-catch block.

Lexical Environment

By the definition of ECMAScript

A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code

When JavaScript engine executes a scope (function, block statement, global), it creates a lexical environment to save the records of that scope. A Lexical environment keeps track of two information: Environment Records and Outer Lexical Environment Reference. Some practical examples.

//Global Environment
var globalVariable = 10;

function functionOne() {
  //Local Environment(Function)
  let functionVariable = 7;
  if (functionVariable > 3) {
    //Local Environment (Block)
    let blockVariable = 4;
    const canNotBechanged = 8;
    console.log(blockVariable); //Output: 4
  }

  function functionThree() {
    //Local Environment (function)
    const printMe = "I am in Console";
    let x = 245;
    console.log(printMe, functionVariable, globalVariable);
    //Output: "I am in Console", 7, 10
  }

  functionThree();
  functionTwo();
}

function functionTwo() {
  var insideFunc = "boom";
  var randVariable = "Naruto";
  console.log(globalVariable); // Output: 10
}

functionOne();

lexicalEnvironment.png

In this example, we can see all the scops (global, local) have their own environment records (variable, function, block scope) saved in the lexical environment, and all the environments also have an extra very important record which is outer. Using the outer record, a child scope can access all of the records from its parent scope.

Scope Chain

When a variable appears in the code, The JS engine searches for that variable's value from the current scope, if it fails it will look into the outer scope and will continue until it reaches the global scope. If we look deep into the functionThree code from the below example

.....

  function functionThree() {
    //Local Environment (function)
    const printMe = "I am in Console";
    let x = 245;
    console.log(printMe, functionVariable, globalVariable);
    //Output: "I am in Console", 7, 10
  }

....

we will understand the scope chain easily. We can see this block of code just consoles three variables printMe, functionVariable, globalVariable.When the JS engine tries to find the value of printMe, it will find it easily in the current scope. After that, it will try to find the value of functionVariable, which is not declared in this current function, so the engine will try to find it in the parent scope (outer scope), which is functionOne, and yes, it will find it there. Finally, the engine will go for globalVariable, which has a global scope declaration. The engine will find the value of glovalVariable same way in the global scope using the lexical environment's records (in this case outer record). This is how the lexical environment chain, or scope chain, is handled.

Scope-2.png

That's it; if you found this article useful, share this article; you can also follow me on Twitter and if you have any questions, please leave a comment! I'd be delighted to assist.