Practical JavaScript Fundamentals
Scope and Variables
Where Your Code Lives
Every line of JavaScript runs inside an Execution Context . Think of it as a container. When you first run a script, JavaScript creates a Global Execution Context. This is the base level, the outermost container for everything in your code.
When a function is called, a new, separate Function Execution Context is created. This new context is stacked on top of the global one. It has its own private space for variables and parameters, which is why a variable inside one function doesn't automatically interfere with a variable in another.
This separation is the core idea behind scope. Scope determines where your variables are accessible. Variables declared in the Global context are available everywhere. Variables declared inside a function are typically only available within that function.
Function vs Block Scope
Originally, JavaScript only had function scope. A variable declared with var is accessible anywhere within the function it's defined in, no matter how many nested blocks (like if statements or for loops) you have.
function checkWeather(temperature) {
if (temperature > 20) {
var message = "It's warm!";
}
// `message` is accessible here, even outside the `if` block.
console.log(message);
}
checkWeather(25); // Logs: "It's warm!"
checkWeather(15); // Logs: undefined
This can cause confusion. In the second function call, message is undefined but doesn't throw an error because var declarations are hoisted to the top of their function. The variable exists, it just hasn't been assigned a value.
ES6 introduced let and const, which use block scope . This is much more intuitive. A variable declared with let or const only exists within the curly braces {} where it was defined. This applies to if statements, for loops, or even just a standalone pair of braces.
function checkWeatherModern(temperature) {
if (temperature > 20) {
let message = "It's warm!";
console.log(message); // Works fine here
}
// console.log(message); // This would throw a ReferenceError!
}
checkWeatherModern(25); // Logs: "It's warm!"
This prevents variables from leaking out of their intended blocks, which is a common source of bugs in larger applications.
Access and Shadowing
Variables declared with let and const have another important feature: the Temporal Dead Zone (TDZ). You cannot access a variable before its declaration in the code. While the JavaScript engine knows the variable will exist, it remains in an uninitialized state until the line where it's declared is executed.
console.log(myVar); // undefined (due to var hoisting)
var myVar = 10;
// --- TDZ in action ---
// console.log(myLet); // Throws ReferenceError!
let myLet = 20;
The TDZ enforces discipline, ensuring you always declare variables before you use them. It helps catch potential errors early.
Another concept to be aware of is variable shadowing. This happens when you declare a variable in an inner scope with the same name as a variable in an outer scope.
let user = "Alice"; // Outer scope variable
function greet() {
let user = "Bob"; // Inner scope variable (shadows the outer one)
console.log("Hello, " + user); // Logs: "Hello, Bob"
}
greet();
console.log(user); // Logs: "Alice"
Inside the greet function, the inner user variable takes precedence. The outer user is temporarily hidden, or "shadowed." Once the function finishes, the outer user is accessible again. While this is a valid language feature, it can sometimes make code harder to read and debug. It's often better to use different, more descriptive variable names to avoid confusion.
Best Practice: Default to
const. Useletonly when you know a variable's value needs to change. Avoid usingvarin modern JavaScript.
const prevents reassignment, making your code more predictable. It signals to other developers (and your future self) that a value is intended to be fixed. Note that for objects and arrays, const only protects the variable assignment itself, not the contents of the object or array.
What is the very first type of execution context created when a JavaScript script begins to run?
What will be logged to the console by the following code snippet?
function testScope() {
if (true) {
var message = "Hello";
}
console.log(message);
}
testScope();
Understanding these scoping rules is fundamental. It governs how data flows through your application and is key to writing clean, reliable code.