JavaScript Scope

In the previous lesson, we learned how a function can remember the variables it was created with. Now let’s look at the rule that makes that possible: scope, which decides where each variable is accessible.

πŸ”­ What is Scope?

Scope is the part of your code where a variable can be used. A variable created in one place is not always visible everywhere else. Where you declare a variable decides who gets to see it.

JavaScript has three kinds of scope.

Scope Where it is created Who can see the variable
Global scope Outside any function or block Every part of your code
Function scope Inside a function Only code inside that function
Block scope Inside a { } block with let or const Only code inside that block

🌍 Global Scope

A variable declared outside every function and block lives in the global scope. Any code in the file can read it.

scope.js
const appName = "Notes"; // global
function showName() {
console.log(appName); // βœ… can see the global variable
}
showName(); // Notes
console.log(appName); // Notes

Let’s walk through what this code does:

  • const appName = "Notes" sits outside every function, so it lives in the global scope.
  • Inside showName, the line console.log(appName) reaches up and reads the global variable without any trouble.
  • The call showName() prints Notes, and the final console.log(appName) at the top level prints Notes too, because global variables are visible everywhere.

πŸ“¦ Function Scope

A variable declared inside a function belongs to that function. Code outside the function cannot see it.

scope.js
function greet() {
const message = "Hello"; // function-scoped
console.log(message); // βœ… Hello
}
greet();
console.log(message); // ❌ ReferenceError: message is not defined

Let’s walk through what this code does:

  • const message = "Hello" is declared inside greet, so it is function-scoped and exists only while greet runs.
  • console.log(message) inside the function prints Hello, because the variable is visible right where it was created.
  • console.log(message) after the call throws a ReferenceError, since outside the function the variable does not exist at all.

🧱 Block Scope

A block is any pair of curly braces, such as the body of an if statement or a loop. When you declare a variable with let or const inside a block, it stays inside that block.

scope.js
if (true) {
let count = 1; // block-scoped
console.log(count); // βœ… 1
}
console.log(count); // ❌ ReferenceError: count is not defined

Let’s walk through what this code does:

  • let count = 1 is declared inside the if block, so it is block-scoped to those curly braces.
  • console.log(count) inside the block prints 1, because the variable is alive within the block.
  • console.log(count) after the block throws a ReferenceError, since the variable disappears the moment the block ends.

The same applies inside a loop. Each variable declared with let or const stays where it was created.

scope.js
for (let i = 0; i < 3; i++) {
const doubled = i * 2;
console.log(doubled); // βœ… 0, 2, 4
}
console.log(i); // ❌ ReferenceError: i is not defined

Let’s walk through what this code does:

  • let i is declared in the loop header, so it is scoped to the loop and its body.
  • const doubled = i * 2 is created fresh on each pass and lives only inside the loop body, printing 0, 2, then 4.
  • console.log(i) after the loop throws a ReferenceError, because i and doubled both vanish once the loop finishes.

var does not respect blocks

let and const are block-scoped, but var is only function-scoped. A var declared inside an if or loop leaks out to the surrounding function. Use let and const so your variables stay where you put them.

πŸ”— The Scope Chain

Inner scopes can read variables from the scopes around them, but outer scopes cannot read variables from inside. JavaScript looks outward, never inward. This outward search is called the scope chain.

scope.js
const outer = "I am outside";
function inner() {
const local = "I am inside";
console.log(outer); // βœ… inner can read the outer variable
}
inner();
console.log(local); // ❌ outside cannot read the inner variable

Let’s walk through what this code does:

  • const outer lives in the global scope, while const local lives only inside inner.
  • console.log(outer) works inside inner, because the inner scope can look outward along the scope chain to find outer.
  • console.log(local) at the top level throws a ReferenceError, since the outer scope cannot look inward into the function.

When code uses a variable, JavaScript first looks in the current scope. If it is not found there, it moves one level out, then keeps moving outward until it reaches the global scope.

🧠 How Scope Powers Closures

The closures from the previous lesson work because of scope. When a function is created inside another function, the inner function keeps access to the outer function’s variables through the scope chain, even after the outer function has finished.

scope.js
function makeCounter() {
let count = 0; // function-scoped
return function () {
count++; // βœ… still reaches the outer variable
return count;
};
}
const next = makeCounter();
console.log(next()); // 1
console.log(next()); // 2

Let’s walk through what this code does:

  • let count = 0 is function-scoped to makeCounter, and the returned inner function reaches it through the scope chain.
  • const next = makeCounter() runs the outer function once and stores the returned inner function, which still holds its link to count.
  • Each next() call runs count++ and returns the new value, printing 1 then 2, because that one count survives between calls.

The returned function still sees count because scope decides what a function can reach, and that link survives after makeCounter returns.

⚠️ Common Mistakes to Avoid

Mistake Problem Solution
Using a function variable outside the function You get a ReferenceError because it is not visible Return the value, or declare it in an outer scope
Expecting a let block variable to exist after the block The variable disappears once the block ends Declare it before the block if you need it after
Using var inside a loop or if It leaks out and can be changed by accident Use let or const for true block scope
Assigning without let or const You create an accidental global variable Always declare with const or let

πŸ”§ Try It Yourself!

  1. Declare a global variable and read it from inside a function.
  2. Declare a variable inside a function and try to read it outside. Watch the ReferenceError.
  3. Declare a let variable inside an if block and try to use it after the block.
  4. Write an outer and inner function, then confirm the inner one can read the outer variable but not the other way around.

🧩 What You’ve Learned

  • βœ… Scope is the part of your code where a variable is accessible
  • βœ… Global scope is visible everywhere; function scope stays inside the function
  • βœ… Block scope keeps let and const variables inside their { }
  • βœ… let and const are block-scoped, while var is only function-scoped
  • βœ… Inner scopes can read outer variables through the scope chain, but not the reverse
  • βœ… Closures work because scope keeps the link to outer variables alive

πŸš€ What’s Next?

Now that you know where variables live, let’s see how JavaScript moves declarations to the top before your code runs. Let’s continue to Hoisting.

Share & Connect