Closures

Frontend SELF EN
Level 37 , Lesson 5
Available

6.1 What Closures Are

Closures are one of the most powerful and important concepts in JavaScript. They let functions remember their lexical environment even after they've been executed. In this lecture, we're gonna dive into what closures are all about, their quirks, and give various examples of how you can use them.

A closure in JavaScript is basically a combination of a function and the lexical environment in which the function was declared. A closure allows a function to "remember" and access variables and other functions within its outer scope even after the outer function has finished running.

Main properties of closures:

  1. Lexical Environment: The context where the function was declared, including all variables available at declaration time.
  2. Context Preservation: A function with a closure can preserve access to variables from its outer scope even after the outer function has finished executing.

6.2 Closure Examples

Example 1: Simple Closure

In this example, innerFunction() has access to the variable outerVariable from its outer scope even after outerFunction() has executed.

JavaScript
    
      function outerFunction() {
        let outerVariable = 'I am from the outer function';

        function innerFunction() {
          console.log(outerVariable);
        }
        return innerFunction;
      }

      const closure = outerFunction();
      closure(); // Logs: I am from the outer function
    
  

Example 2: Counter with a Closure

In this example, the counter function keeps track of the count variable and increments it with each call.

JavaScript
    
      function createCounter() {
        let count = 0;

        return function() {
          count++;
          return count;
        };
      }

      const counter = createCounter();

      console.log(counter()); // Logs: 1
      console.log(counter()); // Logs: 2
      console.log(counter()); // Logs: 3
    
  

Example 3: Closure in a Loop

Closures are often used to preserve variable values in loops.

In this example, each function inside the array arr "remembers" the value of the variable i at the time it's created thanks to block-scoping with let:

JavaScript
    
      function createArrayWithClosures() {
        let arr = [];

        for (let i = 0; i < 3; i++) {
          arr[i] = function() {
            console.log(i);
          };
        }
        return arr;
      }

      const closures = createArrayWithClosures();

      closures[0](); // Logs: 0
      closures[1](); // Logs: 1
      closures[2](); // Logs: 2
    
  

6.3 Complex Use Cases for Closures

Example 1: Partial Application

Closures let you create partially applied functions, with some arguments fixed.

In this example, the multiply() function returns a function that multiplies the given argument b by the fixed argument a.

JavaScript
    
      function multiply(a) {
        return function(b) {
          return a * b;
        };
      }

      const double = multiply(2);
      console.log(double(5)); // Logs: 10
      console.log(double(10)); // Logs: 20
    
  

Example 2: Data Hiding

Closures can be used to create private variables and methods.

In this example, the variables _name and _age are private and can only be accessed through the object's methods:

JavaScript
    
      function createPerson(name, age) {
        let _name = name;
        let _age = age;

        return {
          getName: function() {
            return _name;
          },
          getAge: function() {
            return _age;
          },
          setName: function(newName) {
            _name = newName;
          },
          setAge: function(newAge) {
            _age = newAge;
          }
        };
      }

      const person = createPerson('John', 30);
      console.log(person.getName()); // Logs: John

      person.setName('Jane');

      console.log(person.getName()); // Logs: Jane
      console.log(person.getAge());  // Logs: 30
    
  

Example 3: Memoization

Memoization is an optimization technique where you cache function results to avoid recalculating for the same inputs.

In this example, the memoize() function uses a closure to store a cache of computed results of the fn() function:

JavaScript
    
      function memoize(fn) {
        const cache = {};

        return function(...args) {
          const key = JSON.stringify(args);
          if (cache[key]) {
            return cache[key];
          }
          const result = fn(...args);
          cache[key] = result;
          return result;
        };
      }

      function slowFunction(num) {
        console.log('Computing...');
        return num * 2;
      }

      const memoizedFunction = memoize(slowFunction);

      console.log(memoizedFunction(5)); // Logs: Computing... 10
      console.log(memoizedFunction(5)); // Logs: 10 (result from cache)
    
  
1
Task
Frontend SELF EN, level 37, lesson 5
Locked
Counter with Closure
Counter with Closure
1
Task
Frontend SELF EN, level 37, lesson 5
Locked
Private Variables
Private Variables
1
Survey/quiz
Date, Timer, and Literals, level 37, lesson 5
Unavailable
Date, Timer, and Literals
Date, Timer, and Literals
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION