By WebNest Studio Engineering Team
JavaScript Tutorial
JavaScript Scope
Scope determines where in your code a variable is accessible. JavaScript has three levels of scope — global, function, and block — and understanding how they nest inside one another is essential for predicting which variable a piece of code will actually see.
Scope also explains many of the practical differences between var and let/const covered earlier: the keyword you choose determines which of these scoping rules applies to the variable you declare.
Why it matters
Scope decides where a variable can be seen. A function that accidentally changes a variable outside itself produces bugs that appear far from their cause. Understanding scope is what lets you keep data private to the code that needs it, and it is the foundation for closures.
Global Scope
A variable declared outside of any function or block lives in the global scope and is accessible from anywhere in the program, including inside every function and block. Overusing global scope is generally discouraged, because it makes it easy for unrelated parts of a program to accidentally interfere with each other's data.
Function Scope
A variable declared with var inside a function is scoped to that entire function, regardless of how many nested blocks (if, for, and so on) it passes through. This is why a var declared inside an if block is still visible after that block ends, as long as you remain inside the same function.
Block Scope
A variable declared with let or const is scoped to the nearest enclosing pair of curly braces — an if block, a for loop body, or even a bare { } block on its own. Once execution leaves that block, the variable no longer exists. This tighter scoping is one of the main reasons let and const replaced var in modern JavaScript: it prevents variables from silently "leaking" beyond the block where they logically belong.
Scope Chain and Shadowing
When code refers to a variable, the engine looks for it in the current scope first, then walks outward through each enclosing scope until it finds it (or reaches the global scope and fails, throwing a ReferenceError). If an inner scope declares a variable with the same name as an outer one, the inner variable "shadows" the outer one for the rest of that inner scope.
Examples
var leaking through a block into function scope, versus let staying block-scoped
function demoScope() {
if (true) {
var functionScoped = "visible in the whole function";
let blockScoped = "visible only in this block";
console.log(blockScoped);
}
console.log(functionScoped);
try {
console.log(blockScoped);
} catch (error) {
console.log(error.message);
}
}
demoScope();
visible only in this block
visible in the whole function
blockScoped is not defined
Variable shadowing across nested scopes
let message = "outer";
function showMessage() {
let message = "inner";
console.log(message); // finds the shadowing inner variable first
}
showMessage();
console.log(message); // outer variable is unaffected
inner
outer
Common Mistakes
- Declaring a variable with "var" inside an if block or loop and being surprised it is still accessible after the block ends.
- Assuming block scope applies to "var" the same way it applies to "let" and "const" — "var" only respects function boundaries, not block boundaries.
- Shadowing an outer variable unintentionally by reusing its name in a nested scope, causing confusing bugs when the wrong variable is updated.
- Relying on global variables for values that are only needed temporarily, increasing the risk of naming collisions across a large codebase.
Exercise
Calling show overwrites the outer variable, because the function assigns to it instead of declaring its own. Change the function so that it uses a local variable and the outer one keeps its value.
Expected output: inner then outer
let message = "outer";
function show() {
message = "inner";
console.log(message);
}
show();
console.log(message);
Show hints
- Without a declaration, the assignment looks outward and finds the outer variable.
- Declaring
const messageinside the function creates a separate, local variable that shadows the outer one.
Show solution
let message = "outer";
function show() {
const message = "inner";
console.log(message);
}
show();
console.log(message);
Quick quiz
1. Where can a variable declared with let inside an if block be used?
- A. Only inside that block
- B. Anywhere in the file
- C. Anywhere in the enclosing function
- D. Only after the block
Show answer
A. let and const are block-scoped.
2. When code uses a name, where does JavaScript look for it first?
- A. The global scope
- B. The current scope, then each enclosing scope outward
- C. The window object
- D. The most recently run function
Show answer
B. This outward search is called the scope chain.
3. What is variable shadowing?
- A. Deleting a variable
- B. Declaring a variable twice in one scope
- C. An inner scope declaring a variable with the same name as one in an outer scope
- D. Using a variable before declaring it
Show answer
C. Inside the inner scope the name refers to the inner variable; the outer one is untouched.
Interview questions
What kinds of scope does JavaScript have?
Show answer
Global scope, for names declared outside any function or block. Function scope, for names declared inside a function, including with var. Block scope, for names declared with let or const inside braces, such as a loop or an if. Module scope, where top-level names in a module are private to that module unless exported. JavaScript is lexically scoped: what a function can see is decided by where it is written.
Why are global variables discouraged?
Show answer
Any code anywhere can read or change a global, so it is hard to know what depends on it or what modified it. Two scripts that pick the same global name overwrite each other. Globals also make functions harder to test and reuse, since they depend on state outside themselves. Keeping variables in the smallest scope that needs them, and using modules, avoids these problems.
Key Points to Remember
- JavaScript has global, function, and block scope; the declaration keyword determines which rules apply.
- "var" is function-scoped and ignores block boundaries; "let" and "const" are block-scoped.
- The scope chain resolves a variable by searching the current scope and then each enclosing scope outward.
- Shadowing occurs when an inner scope declares a variable with the same name as one in an outer scope.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering