Function Scope
Scope determines where variables can be accessed in your code. Understanding scope is crucial for writing predictable JavaScript.
Global Scope
javascript// Global variables - accessible everywhere var globalVar = "I'm global"; let globalLet = "I'm also global"; const globalConst = "Me too!"; function showGlobals() { console.log(globalVar); // Accessible console.log(globalLet); // Accessible console.log(globalConst); // Accessible } showGlobals(); console.log(globalVar); // Also accessible here
Function Scope
javascriptfunction myFunction() { var functionVar = "I'm in function scope"; let functionLet = "Me too"; const functionConst = "And me"; console.log(functionVar); // Works console.log(functionLet); // Works console.log(functionConst); // Works } myFunction(); // console.log(functionVar); // Error! Not accessible outside
Block Scope
javascriptfunction testBlockScope() { if (true) { var varVariable = "I'm var"; let letVariable = "I'm let"; const constVariable = "I'm const"; } console.log(varVariable); // Works - var has function scope // console.log(letVariable); // Error - let has block scope // console.log(constVariable); // Error - const has block scope } // Loop block scope for (let i = 0; i < 3; i++) { // i is only accessible within this loop console.log(i); } // console.log(i); // Error! i is not accessible here
Nested Function Scope
javascriptfunction outerFunction() { let outerVariable = "I'm in outer function"; function innerFunction() { let innerVariable = "I'm in inner function"; // Inner can access outer console.log(outerVariable); // Works console.log(innerVariable); // Works function deeperFunction() { // Can access all outer scopes console.log(outerVariable); // Works console.log(innerVariable); // Works } deeperFunction(); } innerFunction(); // console.log(innerVariable); // Error! Can't access inner scope } outerFunction();
Variable Shadowing
javascriptlet name = "Global John"; function testShadowing() { let name = "Function John"; // Shadows global name console.log(name); // "Function John" if (true) { let name = "Block John"; // Shadows function name console.log(name); // "Block John" } console.log(name); // "Function John" again } testShadowing(); console.log(name); // "Global John"
Hoisting and Scope
javascript// var hoisting console.log(hoistedVar); // undefined (not error) var hoistedVar = "I'm hoisted"; // let/const hoisting (Temporal Dead Zone) // console.log(hoistedLet); // ReferenceError let hoistedLet = "I'm not accessible before declaration"; // Function hoisting sayHello(); // Works! Function declarations are fully hoisted function sayHello() { console.log("Hello!"); } // Function expression hoisting // sayGoodbye(); // Error! Cannot access before initialization const sayGoodbye = function() { console.log("Goodbye!"); };
Practical Scope Examples
javascript// Counter with private variables function createCounter() { let count = 0; // Private variable return { increment: function() { count++; return count; }, decrement: function() { count--; return count; }, getCount: function() { return count; } }; } const counter = createCounter(); console.log(counter.increment()); // 1 console.log(counter.increment()); // 2 console.log(counter.getCount()); // 2 // console.log(count); // Error! count is private // Module pattern const userModule = (function() { let users = []; // Private array return { addUser: function(user) { users.push(user); }, getUsers: function() { return [...users]; // Return copy, not original }, getUserCount: function() { return users.length; } }; })(); userModule.addUser({ name: "Alice" }); console.log(userModule.getUserCount()); // 1 // Configuration with scope function createConfig() { const defaultSettings = { theme: "light", language: "en", notifications: true }; let currentSettings = { ...defaultSettings }; return { get: function(key) { return currentSettings[key]; }, set: function(key, value) { currentSettings[key] = value; }, reset: function() { currentSettings = { ...defaultSettings }; } }; }
Common Scope Pitfalls
javascript// Problem: Loop with var for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // Prints 3, 3, 3 (not 0, 1, 2) }, 100); } // Solution 1: Use let for (let i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // Prints 0, 1, 2 }, 100); } // Solution 2: IIFE with var for (var i = 0; i < 3; i++) { (function(index) { setTimeout(function() { console.log(index); // Prints 0, 1, 2 }, 100); })(i); } // Problem: Accidental globals function createUser() { name = "John"; // Oops! No var/let/const creates global let age = 25; // Properly scoped } createUser(); console.log(name); // "John" - accidentally global! // Solution: Always declare variables function createUser() { let name = "John"; // Properly scoped let age = 25; }
Best Practices
javascript// 1. Always declare variables function goodFunction() { let userName = "Alice"; // Good const maxAge = 100; // Good // badVariable = "Bad"; // Avoid - creates global } // 2. Use const by default, let when needed function processData(data) { const processedData = []; // Won't be reassigned let currentIndex = 0; // Will be reassigned // Process data... return processedData; } // 3. Keep scope as narrow as possible function calculateTotal(items) { let total = 0; for (let item of items) { // item is only accessible in this loop if (item.price > 0) { let discountedPrice = item.price * 0.9; // Only needed here total += discountedPrice; } } return total; } // 4. Use functions to create private scope const gameModule = (function() { let score = 0; // Private let level = 1; // Private let playerName = ""; // Private return { // Public methods setPlayerName: function(name) { playerName = name; }, addScore: function(points) { score += points; }, getGameState: function() { return { score: score, level: level, player: playerName }; } }; })();