DEV Community

Michael Mathews
Michael Mathews

Posted on

JavaScript Variable Scope by Example

Variable scope is a fundamental concept that must be mastered in JavaScript. Knowing how var, let, and const behave, and how hoisting and closure affect access to variable values will allow you to write more fluent and predictable code.

What Is Scope?

In JavaScript, scope refers to the current context of execution in which values and expressions are "visible" or accessible.

In JavaScript, there are two main types of scope:

  • Global scope: Variables declared at the "top-level," outside any function or block.
  • Local scope: Variables declared inside a function or block.

JavaScript also has the concept of lexical scoping, which means that a function's access to variables is determined by where it is written in the code, not where it's called from. Inner functions can access variables from their outer (enclosing) functions, creating a nested "scope chain."

var, let, and const

JavaScript provides three keywords for declaring variables: var, let, and const. Each has distinct scoping behaviors.

var: Function Scope and Hoisting

var is function-scoped, which means it can be accessed anywhere within the function in which it is declared (even before its value is initialized). Or if it is used outside of a function, it is global. It is also hoisted, meaning the code acts as if the declaration were moved to the top of its scope at runtime (but not the assignment).

function f() {
  console.log(x); // undefined (not ReferenceError)
  var x = 10;
  console.log(x); // 10
}

f();
console.log(x); // ReferenceError: x is not defined
Enter fullscreen mode Exit fullscreen mode

In the above code, the declaration of x is hoisted to the top of the function, so the first console.log doesn’t throw an error, even though the value assignment hasn’t happened yet. Outside of the function, x does not exist, so it will throw an error.

let and const: Block Scope and Temporal Dead Zone

let and const are block-scoped, meaning they are only accessible within the nearest set of {} braces.

Unlike var, which hoists the variable declaration to the top of its scope, const and let follow a model called The Temporal Dead Zone (TDZ). This refers to the scope between the start of a block and the point where the variable is declared. During this period, the variable exists but cannot be accessed; any attempt to do so will throw a ReferenceError

function demonstrateTdz() {
  console.log(x); // undefined
  console.log(y); // ReferenceError: Cannot access 'y' before initialization
  var x = 1;
  let y = 2;

  console.log(x, y); // 1 2
}

demonstrateTdz();
Enter fullscreen mode Exit fullscreen mode

Using let or const helps avoid confusion caused by hoisting, making code more predictable.

Block Scope Example

{
  let a = 1;
  {
    const b = a + 1; // a is accessible here
    var c = b + 1;
  }
}

console.log(typeof a); // undefined
console.log(typeof b); // undefined
console.log(c);        // 3
Enter fullscreen mode Exit fullscreen mode

In this example, a and b are block-scoped, so they are not available outside their enclosing block. a is available to the inner scope, however. c is declared with var, so it's scoped to the function it is declared in or, as in this case, the global scope.

Closures

A closure is a function scope that maintains access to the variables available in whatever scope it was defined in, regardless of where it is called from.

function makeCounter() {
  let count = 0;

  return function () {
    count++; // from the outer scope
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Enter fullscreen mode Exit fullscreen mode

Closures are fundamental in JavaScript and commonly used in web development, particularly in event handlers and asynchronous code.

Summary

  • var is function-scoped or globally scoped and hoisted.
  • let and const are block-scoped and do not behave as if hoisted (TDZ).
  • Use const or let to limit the scope of your variables and avoid hoisting.
  • Scope and closures are key to many JavaScript patterns, including modules and callbacks.

Top comments (0)