Understanding Scope in JavaScript

Understanding Scope in JavaScript

When programming, understanding the concept of scope is essential as it can help you understand, design, and debug your code better.

In a JavaScript program, a scope is the section of the program where a variable can be accessed. Beyond the environment defined by this scope, it is effectively invisible and therefore, cannot be used or called. This makes it possible for instance, to use the same name for variables declared within different scopes.

Consider this code snippet:

const color = "green";

if (true) {
  const color = "red";
  console.log(color); // OUTPUT: red
}

function printColor() {
  const color = "blue";
  console.log(color); // OUTPUT: blue
}
printColor();

console.log(color); // OUTPUT: green

It might initially seem confusing that the code snippet above runs fine, despite the fact that the color variable seems to have been redeclared and assigned a value three times, using the const keyword (which should be impossible). However, the concept of Scope makes this possible.

Let's now see the types of scope there are.

TYPES OF SCOPE

Types of scope in JavaScript include:

  • Global scope
  • Block scope
  • Function scope
  • Lexical scope

GLOBAL SCOPE

A variable has global scope if it is declared at the top of a program before everything else and outside of any code block or function within that program. This scope is the root level where a program starts to execute by default, and so variables declared here can be accessed at any point throughout the program by functions or statements.

Example:

let color = "green"; // This 'color' variable has Global scope

if (true) {
  console.log(color); // OUTPUT: green
}

function printColor() {
  console.log(color);
}
printColor(); // OUTPUT: green

The color variable is globally scoped and therefore can be accessed within the if statement block and the printColor() function body by the console.log() method.

BLOCK SCOPE

If you declare a variable with the keyword const or let within any code block bounded by braces {}, that variable is block-scoped to this code block. It is local to this block and therefore, only accessible within the environment defined within its opening and closing braces {} and is inaccessible to functions and statements outside it.

Examples of block-scoped variables include those declared within if and while loop statement blocks with either the let or const keyword.

Variables declared with the var keyword are an exception as they have either global scope or are scoped to the closest function within which they are nested.

Example:

if (true) {
  let color = "green"; // This 'color' variable has Block scope
}

{
  let color = "green"; // This 'color' variable has Block scope
}

console.log(color); // OUTPUT: Uncaught ReferenceError: color is not defined

The code snippet above outputs an error message because the console.log() method accepts a parameter, the color variable, which it does not have access to because it was called outside the variable's block scope.

if (true) {
  let color = "green";
  console.log(color); // OUTPUT: green
}

The code snippet above now runs fine without displaying an error message because the console.log() method is now called within the block scope of the variable color, and therefore has access to it.

FUNCTION SCOPE

A variable declared with the const, let or var keyword within a given function's body, is local and scoped to this function. It is only accessible within this function's body and inaccessible outside it. An exception to this is a type of function called a closure (a combination of a function and its lexical environment).

Example:

function printColor() {
  let color = "green"; //This 'color' variable has Function scope
}
printColor();
console.log(color); // OUTPUT: Uncaught ReferenceError: color is not defined

Running the code snippet above outputs an error message because the color variable is scoped to the printColor function, so the console.log() method called outside of this variable's scope does not have access to it.

function printColor() {
  let color = "green"; //This 'color' variable has Function scope
  console.log(color);
}
printColor(); // OUTPUT: green

In the code snippet above, the console.log() method is called within the function scope of the color variable, so it now has access to this variable and the code runs fine.

LEXICAL SCOPE

Within a program, there can be nested combinations of functions, statements and other code blocks. Their scopes are similarly nested within each other such that each scope has access to the outer scope or parent scope it was defined in.

Therefore a variable's lexical scope is an aggregate of its own function or block scope and all the inner scopes nested within it. It can only be accessed within this lexical environment.

Example:

function a() {
  if (true) {
    let color = "green";
  }

  function b() {
    function c() {
      console.log(color); // The 'console.log()' method call is outside the 'color' variable's lexical scope
    }
    c();
  }
  b();
}
a(); // OUTPUT: Uncaught ReferenceError: color is not defined

The code snippet above outputs an error message because the console.log() method is called outside the color variable's lexical scope or not nested within the color variable's block scope, and so has no access to this variable.

function a() {
  if (true) {
    let color = "green";

    function b() {
      function c() {
        console.log(color); // The 'console.log()' method call is within the 'color' variable's lexical scope
      }
      c();
    }
    b();
  }
}
a(); // OUTPUT: green

The code snippet above runs fine without an error because the console.log() method is called within the color variable's lexical scope or nested within the color variable's block scope, and so has access to this variable.

CONCLUSION

A variable's scope determines the section of a program where it can be accessed and in Javascript, there are four kinds of scope- Global scope, which spans the entire program; Block scope, which is restricted to a code block; Function scope, which is strictly restricted to a function's body and Lexical scope, defined by a variable's position in a nested sequence.

Understanding scope is important as it can help you design your code better and more importantly, make your program more secure as variables in your program can be made only accessible in certain parts of it, thus reducing the potential for malicious exploitation or accidental manipulation of data.

It is also for a similar reason, that the let and const keywords introduced in the ES6 version of JavaScript, are now usually recommended over var when declaring variables.

Thanks for reading.