Closures

the lifetime of a variable

the new k-drama coming next fall

// global variables are "alive" and accessible during the life of the application
var bee_gees = 'stayin alive';

// create a new function scope
function childScope(){
  
  // variables declared in here only live as long as this function block statement is executing
  // or, as long as this function is on the stack
  var fruitFly = 1;

  // returns the value of fruitFly, not a reference to the local variable
  return fruitFly;
}

// childScope is has not yet run, it is not yet on the stack
// what variables are currently "alive"?

// now invoke childScope
childScope(); // is now on the stack until it returns

Closures

are created when variables are accessed

outside of the immediate scope

Consider the following

var money = 55.50;
var cupsDrunk = 0;
var coffeeCost = 8.50;

function drinkCoffee(){
  if( money >= coffeeCost ){
    money -= coffeeCost;
    cupsDrunk++;
  }
}

drinkCoffee();
drinkCoffee();
drinkCoffee();

console.log( cupsDrunk );

The original author's intention is to 

increment cupsDrunk by calling 

the drinkCoffee() function, which also depletes money

var money = 55.50;
var cupsDrunk = 0;
var coffeeCost = 8.50;

function drinkCoffee(){
  if( money >= coffeeCost ){
    money -= coffeeCost;
    cupsDrunk++;
  }
}

drinkCoffee();
drinkCoffee();
drinkCoffee();

console.log( cupsDrunk ); // 3
console.log( money ); // 30

The variables are declared in the global scope

what if helpful Jason accesses cupsDrunk directly?

var money = 55.50;
var cupsDrunk = 0;
var coffeeCost = 8.50;

function drinkCoffee(){
  if( money >= coffeeCost ){
    money -= coffeeCost;
    cupsDrunk++;
  }
}

cupsDrunk++;
cupsDrunk++;
cupsDrunk++;

// still 3, looks right
console.log( cupsDrunk );

Helpful Jason inadvertently broke the build!

var money = 55.50;
var cupsDrunk = 0;
var coffeeCost = 8.95;

function drinkCoffee(){
  if( money >= coffeeCost ){
    money -= coffeeCost;
    cupsDrunk++;
  }
}

cupsDrunk++;
cupsDrunk++;
cupsDrunk++;

console.log( money ); // 55.50 !

Closures can be used to prevent that mistake

first, move cupsDrunk into the drinkCoffee() function

so that it's not globally accessible

var money = 55.50;
var coffeeCost = 8.50;

function drinkCoffee(){
  // move the variable into the inner scope
  var cupsDrunk = 0;
  if( money >= coffeeCost ){
    money -= coffeeCost;
    cupsDrunk++;
  }
}

Create a Closure

declare a new function inside the inner scope drinkCoffee()

that has access to all variables in that scope

var money = 55.50;
var coffeeCost = 8.50;

// function expression assigned to drinkCoffee
var drinkCoffee = (function(){
  var cupsDrunk = 0;
  // closure is created when function accesses cupsDrunk
  var checkFundsAndDrink = function(){
    if( money >= coffeeCost ){
      money -= coffeeCost;
      cupsDrunk++; // declared in immediate outer scope
    }
  }
  return checkFundsAndDrink;
})(); // invoke function, returns checkFundsAndDrink

drinkCoffee();
drinkCoffee();
drinkCoffee();

console.log( cupsDrunk ); // error! undefined

Remove temporary variable

the checkFundsAndDrink variable is not really needed

var money = 55.50;
var coffeeCost = 8.50;

var drinkCoffee = (function(){
  var cupsDrunk = 0;
  return function(){ // removed var checkFundsAndDrink
    if( money >= coffeeCost ){
      money -= coffeeCost;
      cupsDrunk++;
    }
  };
})();

drinkCoffee();
drinkCoffee();
drinkCoffee();

console.log( cupsDrunk ); // error! undefined

How do we access cupsDrunk? 

access the  variables in the closure by returning the value

var money = 55.50;
var coffeeCost = 8.50;

var drinkCoffee = (function(){
  var cupsDrunk = 0;
  return function(){
    if( money >= coffeeCost ){
      money -= coffeeCost;
      cupsDrunk++;
    }
    return cupsDrunk; // return the value
  };
})();

var count;
count = drinkCoffee();  // 1
count = drinkCoffee();  // 2
count = drinkCoffee();  // 3
console.log( count );  // 3
console.log( money );  // 30

Closures

By Jon Borgonia

Closures

  • 2,113