JS - 03.04 - Scope of Variable

Bindings and Scopes

In JavaScript, the scope refers to the context in which variables (bindings) are accessible.

Local Variables

A local variable is declared inside a function or block (e.g., {}) and is only accessible within that function or block. Each time the function is called, a new instance of the local variables is created.

function showMessage() {
  let message = "Hello, JS";
  alert(message);
}

showMessage();  // Hello, JS
alert(message);  // Error: message is not defined outside the function

message is local to the showMessage() function, so it cannot be accessed outside of it.

Variables declared inside control structures like if, for, or while using let or const are also block-scoped, meaning they are only visible within that block.

if (true) {
  let blockScoped = "This is block-scoped";
  console.log(blockScoped);  // This will work inside the block
}
console.log(blockScoped);  // Error: blockScoped is not defined outside the block

Variables declared using var, however, have a function scope, meaning they are visible within the function and outside of it if not enclosed in any other block.

Outer Variables (Closure)

A function in JavaScript can access variables from its outer scope (the scope in which it was defined). This allows functions to modify and use variables from the outer environment. This concept is often called closure.

let userName = 'John';

function showMessage() {
  let message = "Hello, " + userName; // Accesses outer variable `userName`
  alert(message);
}

showMessage();  // Hello, John

You can also modify outer variables inside a function:

let userName = 'John';

function showMessage() {
  userName = "Bob";   // Outer variable `userName` is overwritten
  let message = "Hello, " + userName;
  alert(message);  // Hello, Bob
}

alert(userName);  // John (before the function call)
showMessage();
alert(userName);  // Bob (after the function call)

If the function defines a variable with the same name as an outer variable, the inner variable shadows the outer one, meaning the function will use the local variable instead of the outer one.

let userName = 'John';

function showMessage() {
  let userName = "Bob";  // Local variable shadows the outer variable
  let message = "Hello, " + userName;
  alert(message);  // Hello, Bob
}

alert(userName);  // John
showMessage();    // Hello, Bob
alert(userName);  // John

Nested Scope

In JavaScript, functions and blocks can be nested within other functions and blocks, creating nested scopes. Each function or block can access variables from its outer scope, but outer scopes cannot access variables from inner scopes.

const hummus = function(factor) {
  const ingredient = function(amount, unit, name) {
    let ingredientAmount = amount * factor;
    if (ingredientAmount > 1) {
      unit += "s"; // Pluralize the unit if the amount is greater than 1
    }
    console.log(`${ingredientAmount} ${unit} of ${name}`);
  };

  ingredient(1, "can", "Chickpeas");
  ingredient(0.25, "cup", "Tahini");
  ingredient(0.25, "cup", "Lemon Juice");
};

hummus(1);  // Calls hummus and prints ingredient details
  • The inner function ingredient can access the factor argument of the outer hummus function, because the inner function is within the scope of the outer function.
  • The outer function cannot access the local bindings of the inner function, such as ingredientAmount.

This form of scoping is called lexical scoping, meaning that the scope of variables is determined by the position of functions and blocks in the code.

Making a Counter (Closure Example)

A common example of using closures is creating a counter function, which keeps track of the count even after the function has returned. This works because the inner function maintains a reference to the variables in the outer function’s scope.

function makeCounter() {
  let count = 0;

  return function() {
    return count++;  // Accesses and modifies the `count` variable
  };
}

let counter = makeCounter();

alert(counter());  // 0
alert(counter());  // 1
alert(counter());  // 2

Here, the inner function “remembers” the count variable even after the makeCounter() function has finished executing. This is an example of a closure, where the inner function has access to the variables of its outer function. random number generator

Global Variables and the Global Object

A global variable is a variable that is declared outside any function or block. Its scope is the entire program, meaning it is accessible from any function or block within that program (unless shadowed by a local variable with the same name).

let globalVar = 'I am a global variable';

function checkGlobal() {
  alert(globalVar);  // Accessible inside any function
}

checkGlobal();  // I am a global variable

Global variables are often stored in a global object:

  • In a browser, the global object is window.
  • In Node.js, the global object is global.
  • For better compatibility across different environments (e.g., browsers, Node.js), the globalThis keyword is used, which provides a standard way to access the global object in any JavaScript environment.
// Example in a browser environment
window.globalVar = "I am a global variable";  // This variable is a property of the `window` object

console.log(globalThis.globalVar);  // I am a global variable (works in both Node.js and browsers)

Best Practices:

  • Minimize the use of global variables. Having many global variables can lead to potential conflicts and bugs, especially in large applications.
  • Local variables should be preferred wherever possible to avoid polluting the global scope.

Summary of Scoping Rules

  1. Local Scope: Variables declared inside a function or block are local and only accessible within that function or block.
  2. Outer Scope: Functions can access variables from their outer scope. This is known as lexical scoping.
  3. Shadowing: If a local variable has the same name as an outer variable, the local variable shadows the outer variable within that function or block.
  4. Global Scope: Variables declared outside of any function or block are global and accessible from anywhere in the program. In a browser, they are properties of the window object.
  5. Closure: A function that remembers the environment in which it was created, including variables from its outer scope.