Understanding Scope Chain in Javascript

What is the Scope Chain in JS?

JavaScript’s scope chain is a fundamental concept that impacts how variables are accessed and resolved within a program.

Each time a function is invoked or a block is executed, a new scope is created, forming a hierarchical structure. The scope chain represents the order in which JavaScript searches for variables when they are referenced.

When a variable is accessed, the JavaScript engine starts searching for it in the current scope and then follows the chain of parent scopes until the variable is found or until it reaches the global scope.

Scope Chain types

There are different Scope Chain types in JavaScript. Let’s explore each type of the scope chain in detail with illustrative examples.

Global Scope:

The global scope is the outermost scope in a JavaScript program. Variables declared outside of any functions or blocks belong to this scope and can be accessed from any part of the program.

const globalVar = 'I am in the global scope';

function printGlobalVar() {
  console.log(globalVar);
}

printGlobalVar(); // Output: "I am in the global scope"

console.log(globalVar); // Output: "I am in the global scope"

In this example, globalVar is declared in the global scope, so it is accessible both inside the printGlobalVar function and outside it.

Function Scope:

When a function is defined, it creates its own local scope. Variables declared inside a function using the var, let, or const keywords are accessible only within that function’s scope. If a variable is not found in the local scope, JavaScript moves up the scope chain to look for it in the next outer scope.

function outerFunction() {
  const outerVar = 'I am in the outer function';

  function innerFunction() {
    console.log(outerVar);
  }

  innerFunction(); // Output: "I am in the outer function"
}

outerFunction();
console.log(outerVar); // Error: outerVar is not defined

Here, outerVar is declared within the outerFunction, creating a local scope. The innerFunction can access outerVar since it is part of its lexical scope chain.

Block Scope:

ES6 introduced block-scoped variables using let and const. Variables declared using let and const inside a block (e.g. inside loops or conditional statements) are only accessible within that block and its nested blocks.

if (true) {
  let blockVar = 'I am in the block scope';
  console.log(blockVar); // Output: "I am in the block scope"
}

console.log(blockVar); // Error: blockVar is not defined

In this case, blockVar is declared with let inside the block. As a result, it is accessible only within the block and not outside of it.

Lexical Scoping:

JavaScript uses lexical scoping, which means the scope chain is determined by the physical location of functions and blocks in the code. When a function is defined within another function, it forms a nested scope, and the inner function can access variables from the outer function’s scope.

function outerFunction() {
  const outerVar = 'I am in the outer function';

  if (true) {
    const innerVar = 'I am in the inner block';
    console.log(outerVar); // Output: "I am in the outer function"
    console.log(innerVar); // Output: "I am in the inner block"
  }

  console.log(outerVar); // Output: "I am in the outer function"
  console.log(innerVar); // Error: innerVar is not defined
}

outerFunction();

Here, we have both function scope (outerVar) and block scope (innerVar) within the same outerFunction. While outerVar is accessible throughout the function, innerVar is only accessible within the block where it is defined.

Lexical scoping introduces the concept of closures, providing powerful ways to manage state and create private variables in JavaScript functions. Closures continue to have access to the variables from their parent function’s scope even after the parent function has completed execution.

function outerFunction() {
  const outerVar = 'I am in the outer scope';

  function closureFunction() {
    console.log(outerVar); // Forms a closure, accessing outerVar from the outer scope
  }

  return closureFunction;
}

const closure = outerFunction();
// Closure retains access to outerVar even after outerFunction has finished executing.
closure(); // Output: "I am in the outer scope"

Summary

Scope chain is a fundamental concept in JavaScript that plays a crucial role in how variables are accessed and resolved during the execution of code.

Scopes in JavaScript include global scope, function scope, and block scope, each impacting how variables are accessed. Additionally, lexical scoping introduces the concept of closures.

Understanding the scope chain is really important for writing clean and efficient code, especially when dealing with closures, nested functions, and block-scoped variables.


This website is owned by Mateusz Janusz who is a software developer. He writes here about things that he is interested in or is currently learning.
You can find him also at Github and contact on LinkedIn.