Exercise
Variable shadowing occurs when a variable declared in an inner scope has the same name as a variable in an outer scope. The inner variable "shadows" the outer one within its scope.
var name = 'global';
function greet() {
var name = 'local'; // shadows the global 'name'
console.log(name); // Output: local
}
greet();
console.log(name); // Output: global
The Temporal Dead Zone (TDZ) is the period between entering a scope and the point where a let or const variable is declared. Accessing the variable during this period throws a ReferenceError.
// TDZ starts here for 'x'
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 5; // TDZ ends here
A closure is a function that has access to its outer function's variables even after the outer function has returned. The inner function "closes over" the variables in the outer scope, keeping them alive.
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
A closure is created when an inner function retains access to variables from its lexical (outer) scope even after the outer function has finished executing. The closure keeps the scope chain alive.
function outer() {
var secret = 42;
return function inner() {
// inner has a closure over 'secret'
console.log(secret);
};
}
const revealSecret = outer();
revealSecret(); // Output: 42 — outer's scope is preserved
Variables declared with var inside a block (if, for, etc.) are not block-scoped. They are hoisted to the enclosing function scope (or global scope), making them accessible outside the block.
if (true) {
var blockVar = 'I am var';
let blockLet = 'I am let';
}
console.log(blockVar); // Output: I am var
console.log(blockLet); // ReferenceError: blockLet is not defined
An IIFE (Immediately Invoked Function Expression) is a function that is defined and called at the same time. It creates a new function scope, isolating its internal variables from the global scope — a common pattern to avoid polluting global scope.
(function() {
var privateVar = 'I am private';
console.log(privateVar); // Output: I am private
})();
console.log(privateVar); // ReferenceError: privateVar is not defined
Module scope means that variables, functions, and classes declared in an ES6 module are scoped to that module by default. They are not added to the global scope and are not accessible from outside the module unless explicitly exported.
// module.js
const moduleVar = 'only in this module';
export function greet() {
return 'Hello from module';
}
// moduleVar is NOT accessible outside this module unless exported
Global scope means a variable is accessible throughout the entire program. Local scope means a variable is only accessible within the specific function or block where it is declared. Local variables take precedence over global variables of the same name.
var message = 'global'; // global scope
function showMessage() {
var message = 'local'; // local scope — shadows global
console.log(message); // Output: local
}
showMessage();
console.log(message); // Output: global
With var, the loop variable is shared across all iterations because var is function-scoped. With let, each iteration creates a new binding in its own block scope, which is critical for callbacks and closures inside loops.
// Using var — all callbacks share the same 'i'
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // Output: 3, 3, 3
}
// Using let — each iteration has its own 'i'
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0); // Output: 0, 1, 2
Lexical scope (used by JavaScript) means a function's scope is determined by where it is defined in the source code. Dynamic scope (not used in JavaScript by default) would mean a function's scope is determined by where it is called from. JavaScript's this keyword exhibits dynamic-scope-like behaviour depending on the call site.
var x = 'global';
function foo() {
console.log(x); // uses lexical scope — looks where foo was DEFINED
}
function bar() {
var x = 'bar local';
foo(); // Output: global — NOT 'bar local' (lexical, not dynamic)
}
bar();