Scoping in JavaScript Explained | JavaScript Interview Questions

Saturday, February 5, 2022

JavaScript is one of the most popular languages. There are a lot of resources on the web teaching JavaScript and its concepts. Our aim with this series is to share a concise, useful, and quality list of questions with their answers. We don't want to add to the noise but rather be the source of clarity.

Explain Scope in JavaScript

In the most simple terms, scoping/scope is the area in which a variable is accessible. Scoping of a variable is controlled by the location of its declaration. Let us consider an example.

function init() {
  var name = "Devtools Tech";
  console.log(name); // logs Devtools Tech
}
console.log(name); // ReferenceError

In the above code snippet, variable name is declared inside the function init i.e. anywhere inside this function we can get or set the value of the variable. However, accessing the same variable outside the function would lead to ReferenceError: name is not defined. Let us understand why this happens!

Types of Scoping

Block Scope

It is the scope created by a code block in JavaScript. Now, you must be wondering what is a code block?

A (code) block is used to group zero or more statements i.e. any code written inside a pair of curly brackets.

if (true) {
  // code block
  let name = "Devtools Tech";
  let link = "youtube.com/c/devtoolstech";
}

for (let i = 0; i < 4; i++) {
  // code block
  console.log(i);
}

Variables declared using let and const are block-scoped. They are not accessible outside the code block they are declared in. For example --

if (true) {
  let name = "Devtools Tech";
  let link = "youtube.com/c/devtoolstech";

  // logs Devtools Tech youtube.com/c/devtoolstech
  console.log(name, link);
}
console.log(name, link); // Reference Error

In the above code snippet, the variable name and link are only accessible inside the scope created by the if statement. The second console.log(name, link) throws a Reference Error because variables are accessed outside of their scope i.e. the variables doesn't exist there.

The keywords if, for, while create their respective block scope. In JavaScript, we can define standalone code blocks too.

{
  // Block Scoped
  let name = "Devtools Tech";
  const link = "youtube.com/c/devtoolstech";
}
console.log(name, link); // Reference Error

We talked about how let and const are block-scoped variables. However, variables declared using var are not limited to block scope but rather function-scoped. Let us understand more in the next section.

Function Scope

Any function in JavaScript defines a scope for variables declared inside it. The variables could be defined either using var, let, or const.

function init() {
  var name = "Devtools tech";
  const link = "youtube.com/c/devtoolstech";

  if (true) {
    console.log(name, link); // logs the values

    if (true) {
      console.log(name, link); // logs the values
    }
  }

  console.log(name, link); // logs the values
}

console.log(name, link); // ReferenceError

The variables are accessible anywhere inside the function, no matter how deep inside the function they are used. However, if we try to access the same variables outside the function then we would get the ReferenceError as they simply don't exist there.

In some cases, function scope can be considered as the parent scope of the block scope with uni-directional scope access.

function init() {
  // function scope
  const name = "Devtools Tech";

  if (true) {
    // block scope
    const link = "youtube.com/c/devtoolstech";

    console.log(name, link); // logs both the values
  }

  console.log(name); // logs the value
  console.log(link); // ReferenceError
}

In the above code snippet, the variable name is declared in the function scope so it is accessible inside the code block created by the if statement. However, vice versa is not true. The variable link created inside the if statement is block-scoped and cannot be accessed outside. Hence, function scope is acting as the parent scope and you can only access variables in one direction i.e. top to bottom.

Curious Case of var keyword

The variables declared via var bypass the above-mentioned rule. No matter how deep you declare a variable using var. It is always scoped to the nearest function declaration i.e.

function init() {
  if (true) {
    if (true) {
      if (true) {
        if (true) {
          if (true) {
            var name = "Devtools Tech";
          }
        }
      }
    }
  }

  // logs Devtools Tech as name is scoped to the function `init`
  console.log(name);
}

Global Scope

It is the outermost scope. Any variable defined in the global scope is accessible inside any other scope. In a browser environment, any file loaded using the script tag is a global scope.

// file: videos.js

// global scoped variable
function getChannelLink() {
  return "youtube.com/c/devtoolstech";
}
...
<script src="video.js"></script>
<script type="text/javascript">
  // logs the value
  // getChannelLink is accessible here because it is a global variable
  console.log(getChannelLink());
</script>

Different JavaScript runtimes provide different global variables. The browser provides document and window global variables whereas the Node environment provides global variables like process.

Lexical Scope

Let us first take a look at an example

// global scope

function analytics() {
  // outer scope
  const category = "Devtools Tech";

  function track(action) {
    // inner scope
    window.ga("send", {
      eventCategory: category,
      eventAction: action,
    });
  }

  return track;
}

const track = analytics();
track("Question Clicked");

In the above code snippet, the function track invocation happens outside the analytics function scope. Yet, it is still able to access and understand the category variable that is defined inside the scope of the analytics function.

This is due to lexical scoping. JavaScript implements a scoping mechanism called lexical/static scoping. It means that the accessibility of variables are determined by their position within the nested function scopes i.e.

  1. When the JavaScript engine checks for the category variable inside the track function then it first checks its local scope. If found then use it.
  2. If the variable is not found in the local scope then it checks the outer scope. If found then use it.
  3. If not found in the outer scope then it traverses upwards till it either finds the variable or reaches the global scope.

This is what is also known as closure. The lexical scope of the track function consists of the scope of the analytics function.

Added benefits of Scoping

One direct benefit of scoping and using it well is that you can reuse variable names without conflicts.

function iterate(arr) {
  const count = 0;

  while (count !== arr.length) {
    console.log(arr[count]);
    count++;
  }
}

function counter(limit) {
  const count = 1;

  while (count !== limit) {
    console.log(count);
    count++;
  }
}

In the above code snippet, we are using the variable name count in both functions. Since both variables are blocked scope, there won't be any conflict issues.

Final Thoughts

Understanding scoping is essential to write error-free code. It is a fundamental concept. Learning about different scopes and how they impact each other leads to more robust codebases and makes your life easier. I hope this post was useful to you. Do share it with others and help them too! :D

Unsure about your interview prep? Practice Mock Interviews with us!

Book Your Slot Now