JavaScript Closures

  • a function that makes use of variables defined in outer functions that have previously returned
    • if the inner function doesn't use any of the external variables all we have is a nested function
function outer () {
  let data = 'closures are ';
  return function inner() {
    let innerData = 'awesome';
    return data + innerData;
  }
}

outer();      // returns the definition of the inner function
outer()();    // "closures are awesome"
function outer (a) {
  return function inner (b) {
    // the inner function is making use of the variable 'a', which was defined in an outer
    // function called `outer` --- by the time this is called, that outer function has returned
    // this function called `inner` is a closure!
    return a + b;
  }
}

outer(5)(5);                  // 10

let storeOuter = outer(5);
storeOuter(10);               // 15
  • we have to 'return' the inner function for this to work
  • we can either call the inner function right away by using an extra () or we can store the result of the function in a variable (very similar to how bind works)
  • we do NOT have to give the inner function a name --- it can be anonymous (just done in examples for clarity while learning)

Use Closures to Create Private Variables

  • in other languages, there exists support for variables that can't be modified externally , called private variables --- not built-in in JS
function counter () {
  let count = 0;             
  return function () {
    return ++count;           // using prefix operator so it goes up to 1 the first time it is
                              // called
  }
}

counter1 = counter();
counter1();                   // 1
counter1();                   // 2

counter2 = counter();
counter2();                   // 1
counter2();                   // 2

counter1();                   // 3 --- not affected by counter2

count;                        // ReferenceError: count is not defined --- becasue it is private
                              // --- it is private becasue it can't be accessed outside of
                              // `counter` because it isn't scoped outside the function
function classRoom () {
  let instructors = ['Colt', 'Elie'];
  return {
    getInstructors: function () {                   // this is a closure
      return instructors;
    },
    addInstructor: function (instructor) {          // this is also a closure
      instructors.push(instructor);
      return instructors;
    }
  }
}

course1 = classRoom();
course1.getInstructors();         // ["Elie", "Colt"]
course1.addInstructor('Ian');     // ["Elie", "Colt", "Ian"]
course1.getInstructors();         // ["Elie", "Colt", "Ian"]

course2 = classRoom();
course2.getInstructors();         // ["Elie", "Colt"] --- not affected by course1

// we also have NO access to the instructors variable, which makes it private
// no one can modify it...you're stuck with Colt and Elie
// slightly different syntax
let addTo = function (passed) {
  let add = function (inner) {
    return passed + inner;
  } 
  return add;
}

console.log(addTo(3));    // 5

let addThree = addTo(3);
let addOne = addTo(1);

console.log(addThree);      // returns inner function
console.dir(addThree);      // shows us that it is an object --- if look in `[[Scopes]]` and then
                            // look inside `Closure` you can see the `passed` variable, which
                            // means we can use it later

console.log(addThree(3));   // 6
console.log(addOne(2));     // 3
  • NOTE: can play with this last bit of code in the repo: learning-functional-javascript-with-ramda/basic_knowledge/closures

Copyright © 2022