JavaScript Notes
Deep dive into JavaScript scope: global scope, function scope, block scope, lexical scope, scope chain, TDZ, variable shadowing. With memory diagrams, examples, and interview Q&A.
What is Scope?
Scope in JavaScript determines the accessibility and visibility of variables, functions, and objects in your code during runtime. It defines the region of your program where a variable can be referenced and accessed.
Think of scope as a set of rules that governs how the JavaScript engine looks up variables. When you reference a variable, the engine needs to know where to find it — scope provides that answer.
Types of Scope
JavaScript has four types of scope:
- Global Scope - Variables accessible everywhere
- Function Scope - Variables accessible only within a function
- Block Scope - Variables accessible only within a block (ES6+)
- Module Scope - Variables accessible only within a module
Global Scope
Variables declared outside any function or block exist in the global scope. They are accessible from anywhere in your code.
var globalVar = "I am global";
let globalLet = "I am also global";
const globalConst = "I am global too";
function accessGlobal() {
console.log(globalVar);
console.log(globalLet);
console.log(globalConst);
}
accessGlobal();
console.log(window.globalVar); // "I am global" — var becomes window property
console.log(window.globalLet); // undefined — let does NOTI am global I am also global I am global too I am global undefined
In browser environments, var declarations at the top level become properties of the window object. let and const do not.
Global Scope Memory Diagram
+-------------------------------------------+
| GLOBAL SCOPE |
| window.globalVar = "I am global" |
| globalLet = "I am also global" |
| globalConst = "I am global too" |
| |
| +-------------------------------------+ |
| | FUNCTION: accessGlobal | |
| | (can access all above) | |
| +-------------------------------------+ |
+-------------------------------------------+Problems with Global Scope
var name = "Alice";
function greet() {
message = "Hello " + name; // No declaration = implicit global
console.log(message);
}
greet();
console.log(window.message); // "Hello Alice" - leaked!Function Scope
Variables declared inside a function are only accessible within that function.
function calculateArea() {
var width = 10;
let height = 20;
const unit = "px";
console.log(`Area: ${width * height} ${unit}`);
}
calculateArea();
// console.log(width); // ReferenceError
// console.log(height); // ReferenceErrorArea: 200 px
var is Function-Scoped
function example() {
if (true) {
var x = 10; // function-scoped — leaks out of if block
let y = 20; // block-scoped — stays inside if
}
console.log(x); // 10 - accessible!
// console.log(y); // ReferenceError
}
example();10
Nested Function Scope
function outer() {
var outerVar = "outer";
function middle() {
var middleVar = "middle";
function inner() {
var innerVar = "inner";
console.log(outerVar); // accessible
console.log(middleVar); // accessible
console.log(innerVar); // accessible
}
inner();
}
middle();
}
outer();outer middle inner
Block Scope (ES6+)
let and const are block-scoped. A block is any code within curly braces {}.
if (true) {
let blockLet = "block only";
const blockConst = "also block only";
var notBlockScoped = "escapes block";
}
console.log(notBlockScoped); // "escapes block"
// console.log(blockLet); // ReferenceError
// console.log(blockConst); // ReferenceErrorBlock Scope with Loops
var: 3 var: 3 var: 3 let: 0 let: 1 let: 2
Plain Block Scope
{
let x = 42;
const y = "secret";
console.log(x, y); // 42 "secret"
}
// console.log(x); // ReferenceErrorLexical Scope (Static Scope)
JavaScript uses lexical scoping — scope is determined by where functions are written in source code, not where they are called.
let name = "Global";
function outer() {
let name = "Outer";
function inner() {
console.log(name); // Looks in lexical parent
}
return inner;
}
function another() {
let name = "Another";
const fn = outer();
fn(); // "Outer" - not "Another"
}
another();Outer
Lexical Scope Diagram
Source Code Structure determines lookup:
Global: name = "Global"
|
+-- outer(): name = "Outer"
| |
| +-- inner(): console.log(name)
| Lookup: inner -> outer (FOUND "Outer")
|
+-- another(): name = "Another"
Calls inner() but inner was DEFINED inside outer
So inner uses outer's scope, NOT another'sScope Chain
When a variable is referenced, JavaScript searches the scope chain from inner to outer scope.
const global = "G";
function first() {
const a = "A";
function second() {
const b = "B";
function third() {
const c = "C";
console.log(c); // Own scope
console.log(b); // Parent scope
console.log(a); // Grandparent scope
console.log(global); // Global scope
}
third();
}
second();
}
first();C B A G
Scope Chain Resolution Algorithm
Looking up variable 'a' inside third():
1. Check third() local scope -> NOT FOUND
2. Check second() scope -> NOT FOUND
3. Check first() scope -> FOUND! Return "A"
4. (Would check Global if not found)
5. (ReferenceError if not found anywhere)Temporal Dead Zone (TDZ)
Variables declared with let and const exist in a TDZ from the start of their block until the declaration is reached.
console.log(a); // undefined (var hoisted + initialized)
// console.log(b); // ReferenceError (TDZ)
// console.log(c); // ReferenceError (TDZ)
var a = 1;
let b = 2;
const c = 3;TDZ Visualization
{ // TDZ starts for x
// x is in TDZ
// accessing x throws ReferenceError
let x = 10; // TDZ ends, x = 10
console.log(x); // Safe: 10
}TDZ with typeof
console.log(typeof undeclared); // "undefined" - safe
// console.log(typeof tdzVar); // ReferenceError!
let tdzVar = "hello";Variable Shadowing
When an inner scope variable has the same name as an outer scope variable.
let x = 10;
function demo() {
let x = 20; // Shadows outer x
console.log("Function:", x);
if (true) {
let x = 30; // Shadows function x
console.log("Block:", x);
}
console.log("After block:", x);
}
demo();
console.log("Global:", x);Function: 20 Block: 30 After block: 20 Global: 10
Illegal Shadowing
// This is ILLEGAL:
function bad() {
let x = 10;
if (true) {
// var x = 20; // SyntaxError! var would escape block and re-declare let
}
}
// This is LEGAL:
function good() {
var x = 10;
if (true) {
let x = 20; // OK - let stays in block
console.log(x); // 20
}
console.log(x); // 10
}Module Scope
ES6 modules have their own scope. Top-level declarations don't become global.
IIFE for Scope Isolation
(function() {
var private = "hidden";
console.log(private); // "hidden"
})();
// console.log(private); // ReferenceError
// Named IIFE
(function init() {
const config = { debug: false, version: "2.0" };
console.log("Initialized:", config.version);
})();Scope Comparison Table
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (undefined) | Yes (TDZ) | Yes (TDZ) |
| Re-declaration | Allowed | Not allowed | Not allowed |
| Re-assignment | Allowed | Allowed | Not allowed |
| window property | Yes | No | No |
| TDZ | No | Yes | Yes |
Common Mistakes
Mistake 1: var is not block-scoped
Mistake 2: Accidental globals
function oops() {
leaked = "global now"; // Missing let/const/var
}
oops();
console.log(leaked); // "global now"Mistake 3: TDZ confusion
let value = "outer";
function test() {
// console.log(value); // ReferenceError (TDZ from block start)
let value = "inner"; // This shadows and creates TDZ above
console.log(value); // "inner"
}Common Traps 🪤
| Pattern | Unexpected Behavior | Fix |
|---|---|---|
for(var i...) callbacks | All see final i value | Use let i |
if{var x} outside | x accessible outside if | Use let |
| No declaration keyword | Accidental global variable | Use strict mode |
| TDZ shadow | Inner let shadows outer before line | Rename or reorder |
for...in + var | Loop variable leaks to outer | Use let |
Interview Questions
Q1: Output?
var x = 1;
function foo() {
console.log(x);
var x = 2;
}
foo();Answer: undefined — local var x is hoisted, shadows global.
Q2: Output?
let a = 1;
{
console.log(a);
let a = 2;
}Answer: ReferenceError — TDZ for inner let a.
Q3: Output?
Answer: 3 then ReferenceError.
Q4: Explain scope chain
Answer: The scope chain is the ordered list of variable objects that the engine searches when resolving a variable name. It starts with the current scope and extends outward through each enclosing scope until reaching global.
Q5: Output?
function outer() {
var x = 10;
function inner() {
console.log(x);
}
x = 20;
return inner;
}
outer()();Answer: 20 — closure captures the variable reference, not the value at creation time.
Best Practices
- Always use
constby default,letwhen reassignment needed - Never use
varin modern code - Minimize global variables to avoid collisions
- Use modules for encapsulation
- Declare variables at the top of their scope
- Avoid shadowing unless intentional
- Use strict mode to catch accidental globals
Summary
| Concept | Key Point |
|---|---|
| Global Scope | Accessible everywhere, pollutes namespace |
| Function Scope | var lives within entire function |
| Block Scope | let/const live within {} |
| Lexical Scope | Determined by source code position |
| Scope Chain | Inner to outer variable lookup |
| TDZ | let/const inaccessible before declaration |
| Shadowing | Inner variable hides outer same-name |
| Module Scope | Top-level stays private to module |
Practice Exercises
- Predict outputs of nested scope examples
- Refactor var code to use let/const properly
- Identify scope bugs and fix them
- Create module with controlled public API
- Demonstrate all four scope types in one file
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Scope in JavaScript — Complete Guide with Examples 2026.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this JavaScript Master Course topic.
Search Terms
javascript-complete-guide, javascript master course, javascript, complete, guide, advanced, scope, scope in javascript — complete guide with examples 2026
Related JavaScript Master Course Topics