Scopes

In JavaScript, scope refers to the area where a variable is accessible. It determines where in your code a particular variable can be used or modified. Think of scope as a boundary — variables defined inside a boundary can only be used within that boundary.

Understanding scope is important because it helps you avoid bugs caused by variables being accidentally changed or accessed from unexpected parts of your code.

javascript

// 'name' is accessible here because it is in the global scope
let name = "JavaScript";

function greet() {
  // 'name' is accessible inside this function too
  console.log("Hello, " + name);
}

greet(); // Output: Hello, JavaScript

Scoping is the process by which the JavaScript engine determines where variables and functions are accessible. When you write code, the engine uses scoping rules to figure out which variable you are referring to at any given point in the program.

JavaScript uses a set of rules to look up variables. It first checks the current scope. If the variable is not found there, it moves to the outer scope, and keeps going until it reaches the global scope. If the variable is still not found, a ReferenceError is thrown.

javascript

let language = "JavaScript";

function outer() {
  let framework = "React";

  function inner() {
    // 'inner' can access 'framework' from outer() and 'language' from global scope
    console.log(language);  // Output: JavaScript
    console.log(framework); // Output: React
  }

  inner();
}

outer();

JavaScript follows lexical scoping (also called static scoping). This means that the scope of a variable is determined by where it is written in the source code, not by where the function is called from.

In simple terms, a function can access variables from its own scope and from any outer (parent) scope where it was defined. This is what makes closures possible in JavaScript.

javascript

function outerFunction() {
  let outerVar = "I am from outer function";

  function innerFunction() {
    // innerFunction can access outerVar because of lexical scoping
    console.log(outerVar);
  }

  return innerFunction;
}

const myFunc = outerFunction();
myFunc(); // Output: I am from outer function

In the example above, innerFunction can still access outerVar even after outerFunction has finished running. This is because lexical scoping keeps the reference to the scope where the function was originally defined.

JavaScript has three main types of scope:

  • Global Scope — Variables declared outside any function or block.
  • Functional Scope — Variables declared inside a function using var, let, or const.
  • Block Scope — Variables declared inside a block ({}) using let or const.

Let's look at each type in detail.

A variable has global scope when it is declared outside of any function or block. Global variables can be accessed from anywhere in the program — inside functions, inside blocks, or in the top-level code.

javascript

// Global variable
let color = "blue";

function printColor() {
  console.log(color); // Output: blue
}

printColor();
console.log(color); // Output: blue

While global variables are useful for sharing data across your program, using too many can lead to naming conflicts and hard-to-find bugs. It's a good practice to keep the number of global variables to a minimum.

When a variable is declared inside a function, it has functional scope (also called function scope or local scope). This means the variable can only be accessed inside that function. It is not available outside of it.

javascript

function showMessage() {
  let message = "Hello from inside the function!";
  console.log(message); // Output: Hello from inside the function!
}

showMessage();
console.log(message); // ReferenceError: message is not defined

Variables declared with var, let, or const inside a function are all function-scoped. They cannot be accessed from outside the function.

Functional scope also means nested functions can access variables from their parent function:

javascript

function outer() {
  let x = 10;

  function inner() {
    let y = 5;
    console.log(x + y); // Output: 15
  }

  inner();
  console.log(y); // ReferenceError: y is not defined
}

outer();

Block scope was introduced in ES6 (ECMAScript 2015) with the let and const keywords. A block is any code inside curly braces {}, such as if statements, for loops, or while loops.

Variables declared with let or const inside a block are only accessible within that block. Variables declared with var are not block-scoped — they are function-scoped.

javascript

if (true) {
  let a = 10;
  const b = 20;
  var c = 30;
}

console.log(c); // Output: 30 (var is NOT block-scoped)
console.log(a); // ReferenceError: a is not defined
console.log(b); // ReferenceError: b is not defined

Here is another example using a for loop:

javascript

for (let i = 0; i < 3; i++) {
  console.log(i); // Output: 0, 1, 2
}

console.log(i); // ReferenceError: i is not defined

// Compare with var
for (var j = 0; j < 3; j++) {
  console.log(j); // Output: 0, 1, 2
}

console.log(j); // Output: 3 (var is NOT block-scoped)

Using let and const instead of var is recommended because block scoping makes your code more predictable and easier to debug.

  • Scope determines where a variable can be accessed in your code.
  • Scoping is the process the engine uses to look up variables.
  • Lexical scoping means scope is determined by where the code is written, not where it is called.
  • Global scope — variables declared outside any function or block, accessible everywhere.
  • Functional scope — variables declared inside a function, accessible only within that function.
  • Block scope — variables declared with let or const inside a block ({}), accessible only within that block.
  • var is function-scoped, while let and const are block-scoped.