Scope Chain in JavaScript
The scope chain is how JavaScript resolves variable names. It's the set of rules that determines how JavaScript looks up variables and where they can be accessed.
Lexical Scope
// Variables are accessible from inner functions
const globalVar = 'I am global';
function outerFunction() {
const outerVar = 'I am from outer';
function innerFunction() {
const innerVar = 'I am from inner';
console.log(innerVar); // Accessible
console.log(outerVar); // Accessible
console.log(globalVar); // Accessible
}
innerFunction();
console.log(innerVar); // ReferenceError
}
outerFunction();
console.log(outerVar); // ReferenceErrorBlock Scope
// let and const create block scope
{
let blockVar = 'block scoped';
const constVar = 'also block scoped';
var functionVar = 'function scoped';
}
console.log(functionVar); // Accessible
console.log(blockVar); // ReferenceError
console.log(constVar); // ReferenceError
// Loop scope
for (let i = 0; i < 3; i++) {
const square = i * i;
console.log(square);
}
console.log(i); // ReferenceError
console.log(square); // ReferenceErrorFunction Scope
// var creates function scope
function functionScope() {
var functionVar = 'function scoped';
let blockVar = 'block scoped';
if (true) {
var anotherFunctionVar = 'still function scoped';
let anotherBlockVar = 'block scoped';
}
console.log(functionVar); // Accessible
console.log(anotherFunctionVar); // Accessible
console.log(anotherBlockVar); // ReferenceError
}
// Hoisting with var
function hoistingExample() {
console.log(hoistedVar); // undefined
var hoistedVar = 'hoisted';
}Closure and Scope
// Closures retain their scope chain
function createCounter() {
let count = 0; // Enclosed variable
return {
increment() {
return ++count; // Access to parent scope
},
decrement() {
return --count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.count); // undefinedModule Scope
// Each module has its own scope
// moduleA.js
const privateVar = 'private';
export const publicVar = 'public';
// moduleB.js
import { publicVar } from './moduleA.js';
console.log(publicVar); // Accessible
console.log(privateVar); // ReferenceError
// Global scope pollution
window.globalVar = 'polluting global scope';
console.log(globalVar); // Accessible everywhereCommon Interview Follow-up Questions
- How does the scope chain work with nested functions?
- What's the difference between lexical and dynamic scope?
- How does block scope differ from function scope?
- How do closures maintain their scope chain?
Best Practices
- Prefer const and let over var
- Keep functions pure and avoid global scope
- Use modules to encapsulate code
- Be mindful of closure memory implications
- Understand hoisting behavior
- Use block scope for better variable isolation