JS Variable Scope

JavaScript Variable Scope: Local vs Global

In this section, we will discuss a topic that is often considered challenging for beginners: Variable Scope.

Scope defines exactly where you can access a certain variable in your code.

We will explore this concept by looking at local variables, global variables, and the specific differences between let, const, and var.


1. Local Variables in Functions

Local variables are only in scope within the function they are defined in.

This means if you create a variable inside a function, it is invisible and inaccessible to the outside world. This rule applies to both variables you create using let and var, as well as the function parameters themselves.

Local Scope Example

function testAvailability(x) {
    let y = "Local variable!";
    console.log("Available inside:", x);
    console.log("Available inside:", y);
}

testAvailability("Hi!");

// These lines will cause errors! console.log("Not available outside:", x); // ReferenceError console.log("Not available outside:", y); // ReferenceError

As shown above, the variables x and y are out of scope outside the function, but completely in scope inside the function.

Returning Local Values

For beginners, combining local variables and the return statement can be confusing.

While local variables declared inside a function are absolutely not available outside, you can use return to pass their value to the outside. The key word here is values! You cannot return the variable itself, but the value can be caught and stored in a completely different variable outside.

Returning Local Values Example

function testAvailability() {
    let y = "I'll return";
    console.log("Inside:", y);
    return y; // Sending the value outside
}

// Catching the returned value in a new variable 'z' let z = testAvailability();

console.log("Outside the function:", z); // Outputs: "I'll return"

(Note: We could have also named the outside variable y, but that would be confusing since it still would have been a completely separate, different variable from the one inside the function.)


2. let vs var Variables

There is a very important difference between let and var when it comes to scope:

A block is defined by two curly braces { } (like an if statement or a for loop). The code within those braces is the only place where let is available.

The var Scope (Function Scoped)

If we use var, the variable is available anywhere within the entire function, even after the specific block it was created in has ended.

`var` Scope Example

function doingStuff() {
    if (true) {
        var x = "local"; // Created inside the 'if' block
    }
    console.log(x); // Still accessible here! Outputs: "local"
}

doingStuff();

The let Scope (Block Scoped)

Here is what happens when we try the exact same thing using let:

`let` Scope Example

function doingStuff() {
    if (true) {
        let x = "local"; // Created inside the 'if' block
    }
    console.log(x); // ERROR! x is out of scope here.
}

doingStuff(); // Results in ReferenceError: x is not defined

Because let is block-scoped, x ceases to exist the moment the if block's closing brace } is reached.

Hoisting Differences

A final difference relates to the order of declaration. If you try to use a let variable before you declare it on the lines below, JavaScript will throw a ReferenceError (it is in the "Temporal Dead Zone").

If you try to use a var variable before you declare it, you won't get an error; instead, it will simply be undefined. This is due to a complex phenomenon called hoisting, which we will cover in intermediate chapters.


3. const Scope

Constants declared with const follow the exact same scoping rules as let.

They are block-scoped, meaning they disappear as soon as the block { } they were defined in is finished. Using a const variable before it is defined will also throw a ReferenceError.


4. Global Variables

As you might have guessed, global variables are variables declared outside of any function or code block.

A variable defined at the top level of your program is available everywhere in your program.

Global Variable Example

let globalVar = "Accessible everywhere!";
console.log("Outside function:", globalVar);

function creatingNewScope() { console.log("Inside function:", globalVar); }

creatingNewScope();

Shadowing (Hiding) Global Variables

You can "hide" a global variable inside a function by specifying a new local variable with the exact same name. If you do this, the function will use the local value instead of the global one.

Variable Shadowing Example

let x = "global";

function doingStuff() { let x = "local"; // This hides the global 'x' inside this function console.log("Inside function:", x); // Outputs: local }

doingStuff(); console.log("Outside function:", x); // Outputs: global

(Note: The same behavior occurs if a function parameter shares the same name as a global variable. The parameter value will override the global variable inside the function.)

The Danger of Implicit Globals

There is a danger in relying on global variables too much, and a specific bad practice you must avoid. Look at the following code:

Implicit Global (Bad Practice!)

function confuseReader() {
    x = "Guess my scope..."; // Notice no 'let', 'var', or 'const'
    console.log("Inside:", x);
}

confuseReader(); console.log("Outside:", x); // Outputs: "Guess my scope..."

Why did the outside console log work? Because x was defined without let, var, or const. When this happens, JavaScript automatically assigns the variable to the global scope, even if it was created deep inside a function!

Warning: Creating implicit global variables is a terrible practice. It leads to incredibly confusing bugs. If you need a global variable, declare it explicitly with let or const at the top of your file!


5. Immediately Invoked Function Expression (IIFE)

An Immediately Invoked Function Expression (IIFE) is a way of writing an anonymous function (a function without a name) that executes itself instantly.

This pattern is frequently used to initialize code and to create private variables that cannot be accessed from the outside global scope.

Defining an IIFE

You define an IIFE by wrapping the entire function inside parentheses (), and then adding an executing pair of brackets (); at the very end.

IIFE Example

(function () {
    let privateVar = "I am safe and private!";
    console.log("IIFE executed right away!");
})();

If you use Arrow Functions, it becomes even more concise:

(() => {
    console.log("Arrow IIFE ran right away!");
})();

Exercise

?

What is the primary scoping difference between the let and var keywords?