Course: JavaScript Intermediate
Lesson 1: Scope and Where Variables Exist
Level
Intermediate
Estimated Reading Time
10–15 minutes
Goal
By the end of this lesson, the learner will be able to:
- Explain the difference between block scope, function scope, and global scope
- Use
constandletcorrectly within blocks and functions - Identify when a variable is accessible and when it causes a ReferenceError
Frontend Development Context
In frontend development, variable scope determines which parts of your code can read or modify a value. When you write a click handler, a loop, or a conditional block, the variables you declare inside those structures are not always available outside them. Understanding scope helps you avoid bugs where a variable seems to disappear or holds an unexpected value.
Scope also affects how you organize code into functions and modules. A variable declared at the top of a file is globally scoped and accessible everywhere — which can be convenient but also dangerous if multiple parts of the app modify it. Keeping variables scoped as narrowly as possible is a core discipline in writing maintainable JavaScript.
Explanation
JavaScript has three main levels of scope: global, function, and block. A variable declared outside any function or block lives in global scope and can be accessed anywhere in the script. A variable declared inside a function is scoped to that function and cannot be accessed outside it. A variable declared inside a block — any pair of curly braces {} — using let or const is scoped to that block only.
The var keyword behaves differently from let and const. Variables declared with var are function-scoped, not block-scoped. This means a var declared inside an if block leaks out to the surrounding function, which is rarely what you want. This is one reason var is avoided in modern JavaScript.
const and let respect block boundaries. If you declare const score = 0 inside an if block, that variable does not exist before the block starts or after it ends. Trying to access it outside the block produces a ReferenceError.
Function scope creates a new isolated environment. Variables declared inside a function are local to that function. They are created when the function runs and destroyed when it finishes. This allows you to reuse variable names like i or result in different functions without conflicts.
Nested scopes follow a chain: inner scopes can read variables from outer scopes, but outer scopes cannot access variables from inner scopes. This lookup chain is called the scope chain, and understanding it helps you reason about which value a variable holds at any point in your code.
Code Example
This example shows global scope, function scope, and block scope in action side by side.
const appName = "LearnSkills"; // global scope
function showLessonInfo(lessonTitle) {
const prefix = "Lesson:"; // function scope
if (lessonTitle) {
const message = `${prefix} ${lessonTitle} — ${appName}`; // block scope
console.log(message); // works fine here
}
// console.log(message); // ReferenceError: message is not defined
console.log(prefix); // works — still inside the function
}
showLessonInfo("Scope Basics");
// console.log(prefix); // ReferenceError: prefix is not defined
console.log(appName); // works — global scope
Code Explanation
appName is declared at the top level outside any function or block, making it globally scoped. Both the function body and the if block can read it freely because inner scopes can look outward along the scope chain.
prefix is declared inside showLessonInfo. It is function-scoped — accessible anywhere inside that function but not outside it. Attempting to log prefix after the function call would throw a ReferenceError.
message is declared with const inside the if block. It only exists within those curly braces. The commented-out line shows what would happen if you tried to access it after the block closes — the variable no longer exists at that point.
This layered structure illustrates how JavaScript resolves variable names by looking outward: the if block can see prefix and appName, but nothing outside can see message or prefix.
Pattern Highlights
✅ Positive Pattern: Declare variables with
constorletas close to where they are used as possible. This limits their scope and makes code easier to reason about.
⚠️ Neutral Note: Inner scopes can read variables from outer scopes. This is useful but can make code harder to trace if overused — keep outer variables focused and clearly named.
❌ Negative Pattern: Avoid using
varinside blocks expecting block-scoping behavior. Avardeclared inside anifblock is still accessible in the surrounding function, which leads to subtle bugs.
Common Mistakes
Mistake 1: Using var inside a block and expecting it to be block-scoped
Developers coming from other languages often expect var to behave like let.
if (true) {
var count = 5;
}
console.log(count); // 5 — unexpected! var is not block-scoped
Because var is function-scoped (or globally scoped when outside a function), it leaks out of the if block. Use let or const instead.
Mistake 2: Accessing a const or let variable before its declaration line
Assuming a variable exists just because it appears somewhere in the function.
function setup() {
console.log(label); // ReferenceError: Cannot access 'label' before initialization
const label = "Active";
}
setup();
const and let variables are not accessible before their declaration. This is the temporal dead zone. Always declare before you use.
Mistake 3: Shadowing an outer variable unintentionally
Declaring a new variable with the same name in an inner scope hides the outer one.
const status = "online";
function checkStatus() {
const status = "offline"; // shadows outer status
console.log(status); // "offline"
}
checkStatus();
console.log(status); // "online" — outer is unchanged, but the shadowing is confusing
While shadowing is not an error, it makes code harder to read. Use distinct names to avoid confusion.
Quick Recap
- Global scope means a variable is accessible everywhere in the script
- Function scope means a variable only exists inside the function that declares it
- Block scope with
constorletmeans a variable only exists inside the{}block that contains it varis function-scoped and leaks out of blocks — preferconstandletin all modern code