What is Scope?

Scope determines the accessibility or visibility of variables and functions at various parts of your code during runtime. In simple terms, it's a set of rules for storing and finding variables.

  • Global Scope: Variables declared outside of any function or block are in the global scope. They can be accessed from anywhere in your application. It's best to minimize the use of global variables to avoid conflicts.

JavaScript

const appName = "My Awesome App"; // Global variable

function showAppName() {
  console.log(`Welcome to ${appName}!`); // Can access it here
}
showAppName();
  • Function Scope: Variables declared with var inside a function are only accessible within that function.


JavaScript

function calculate() {
  var secretValue = 42; // Function-scoped
  console.log(secretValue);
}
calculate();
// console.log(secretValue); // This would cause a ReferenceError
  • Block Scope: Variables declared with let and const inside a block (code enclosed in curly braces {}) are only accessible within that block. This is a more predictable and robust way to scope variables.


if (true) {
  let blockScopedVar = "I'm only visible here!";
  const alsoBlockScoped = "Me too!";
  console.log(blockScopedVar);
}
// console.log(blockScopedVar); // ReferenceError

What is a Closure?

A closure is one of JavaScript's most powerful and often-misunderstood features. Here’s a simple definition:

A closure is a function that remembers the environment (the scope chain) in which it was created. It gives an inner function access to its outer function's scope, even after the outer function has finished executing and returned.

Let's see this with a classic example: a function factory.

Code Snippet: A Counter Closure We'll create a function that makes counter functions.

JavaScript


function createCounter() {
  let count = 0; // 'count' is in the outer function's scope.

  // This inner function is returned
  const increment = function() {
    count++; // It has access to 'count'
    console.log(count);
  };
  
  return increment;
}

// 1. Call the outer function. It runs, creates 'count', and returns the 'increment' function.
const counterA = createCounter();

// 2. The 'createCounter' function is done. Its scope should be gone, right?
// But 'counterA' is a closure. It holds a reference to its creation environment,
// including the 'count' variable.

counterA(); // 1
counterA(); // 2
counterA(); // 3

// We can create a completely separate counter with its own 'count' variable.
const counterB = createCounter();
counterB(); // 1

In this example, counterA is a closure. It "closes over" the count variable from its parent scope (createCounter). This allows it to maintain its own private state, a fundamental pattern for creating encapsulated modules and data privacy in JavaScript.