JavaScript Scopes

Scope controls where your variables live and where you can use them. Picture a room with walls: variables created inside that room stay inside it.

Once you understand scope, you'll avoid a whole category of bugs where variables get changed from places you didn't expect.

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 how JavaScript figures out which variable you mean. When your code runs, the engine follows a set of rules to find the right variable.

It works like a search: JavaScript checks the current scope first, then moves outward one level at a time until it reaches the global scope. If it still can't find the variable, you get a ReferenceError.

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 uses lexical scoping (also called static scoping). This means where you write your code decides the scope, not where you call it from.

A function can always reach variables in its own scope and any parent scope where it was defined. This is what makes closures work 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

Notice that innerFunction can still reach outerVar even after outerFunction has finished running. Lexical scoping remembers the scope where the function was originally written.

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.
JavaScript Scope - Global, Function and Block Scope diagram

Let's look at each type in detail.

When you declare a variable outside of any function or block, it has global scope. That means every part of your program can read and use it.

javascript

// Global variable
let color = "blue";

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

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

Global variables are handy for sharing data, but too many of them can cause naming conflicts and sneaky bugs. Try to keep them to a minimum.

If you create a variable inside a function, it has functional scope (also called local scope). Only code inside that function can see it. Outside the function, it doesn't exist.

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

It doesn't matter whether you use var, let, or const. If you declare a variable inside a function, it stays inside that 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();

ES6 introduced block scope with let and const. A "block" is any code wrapped in curly braces {}, like if statements, for loops, or while loops.

Variables you create with let or const inside a block stay inside that block. But var ignores block boundaries. It's function-scoped instead.

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)

Stick with let and const over var whenever you can. Block scoping keeps your code predictable and much 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.

What's next? Now that you understand where variables live, let's start working with reusable code in the next tutorial.

Videos for this topic will be added soon.