Get started with 33% off your first certification using code: 33OFFNEW

Understanding Scope and Closure in JavaScript

4 min read
Published on 7th April 2023
Understanding Scope and Closure in JavaScript

Scope and closure are fundamental concepts in JavaScript that every developer should understand. They dictate how variables can be accessed and manipulated within your code. In this article, we'll explore scope and closure in JavaScript, provide code examples to illustrate the concepts, and offer resources to help you gain a deeper understanding.

Scope

In JavaScript, scope determines the visibility and accessibility of variables, functions, and objects within your code. There are two primary types of scope in JavaScript:

  1. Global scope: Variables and functions declared outside of any function or block are in the global scope. They can be accessed from anywhere in your code, including within functions and blocks.
  2. Local scope (Function scope): Variables and functions declared within a function are in the local scope of that function. They can only be accessed within that function and any nested functions.

Example:

const globalVar = 'I am in the global scope';

function exampleFunction() {
  const localVar = 'I am in the local scope';

  console.log(globalVar); // Output: I am in the global scope
  console.log(localVar); // Output: I am in the local scope
}

exampleFunction();
console.log(localVar); // Error: localVar is not defined

Block Scope

JavaScript also has block scope, which was introduced with the let and const keywords in ECMAScript 2015 (ES6). Variables declared with let and const are block-scoped, meaning they are only accessible within the block in which they are declared.

Read our tutorial for the differences between let, const and var.

Example:

if (true) {
  const blockVar = 'I am in the block scope';
  console.log(blockVar); // Output: I am in the block scope
}

console.log(blockVar); // Error: blockVar is not defined

Lexical Scope

JavaScript has lexical scoping, meaning that functions can access variables declared within their own scope and any parent scopes. This allows nested functions to "remember" their parent's variables, even if the parent function has completed execution.

Example:

function outerFunction() {
  const outerVar = 'I am in the outer function';

  function innerFunction() {
    console.log(outerVar); // Output: I am in the outer function
  }

  innerFunction();
}

outerFunction();

Closures

Closures are a powerful feature in JavaScript that allow functions to "remember" the environment in which they were created. A closure is created when a nested function references a variable from its containing function. This allows the nested function to continue accessing the variable even after the containing function has completed execution.

Example:

function createGreeting(greeting) {
  return function (name) {
    console.log(greeting + ', ' + name);
  };
}

const sayHello = createGreeting('Hello');
sayHello('John'); // Output: Hello, John

In this example, the createGreeting function returns a nested function that has access to the greeting variable. When we call sayHello('John'), the nested function still has access to the greeting variable, even though createGreeting has completed execution.

Practical Applications of Closure

Closures have various practical applications in JavaScript, such as:

  1. Module pattern: Closures can be used to create private variables and methods within an object, emulating the functionality of classes in other programming languages.
  2. Function factories: Closures allow you to create functions that generate other functions with specific behavior or configuration, as demonstrated in the createGreeting example above.
  3. Memoization: Closures can be used to implement memoization, a technique for caching the results of expensive function calls to improve performance.

Example of the module pattern:

const counterModule = (function () {
  let count = 0;

  function increment() {
    count++;
    console.log(count);
  }

  function reset() {
    count = 0;
    console.log('Counter reset');
  }

  return {
    increment: increment,
    reset: reset,
  };
})();

counterModule.increment(); // Output: 1
counterModule.increment(); // Output: 2
counterModule.reset(); // Output: Counter reset

In this example, the counterModule object is created using an immediately invoked function expression (IIFE) that takes advantage of closures to create private variables and methods.

Additional Resources

To further deepen your understanding of scope and closure in JavaScript, consider exploring the following resources:

  1. MDN Web Docs: Closures - A comprehensive guide to closures in JavaScript, complete with examples and detailed explanations.
  2. You Don't Know JS: Scope & Closures - A book by Kyle Simpson that covers scope and closures in depth, as part of the popular "You Don't Know JS" series.

However, if you feel like you're comfortable with these JavaScript concepts then check out our JavaScript Certification, which can help you land that new job or promotion by proving your knowledge.

Summary

Understanding scope and closure is essential for writing clean, maintainable JavaScript code. By grasping the concepts of global and local scope, block scope, lexical scope, and closures, you'll be better equipped to create efficient, modular applications. Don't forget to practice your newfound knowledge with real-world examples, and refer to the resources provided for a deeper understanding of these fundamental JavaScript concepts.