Skip to content

Instantly share code, notes, and snippets.

@ross-u
Created June 2, 2020 12:42
Show Gist options
  • Save ross-u/ae3f5f5eb704fbd0eef0f6e6edc02b2b to your computer and use it in GitHub Desktop.
Save ross-u/ae3f5f5eb704fbd0eef0f6e6edc02b2b to your computer and use it in GitHub Desktop.
JS | Closure - ES6 examples with class

JS | Closure, IIFE and Module Pattern


Terminology :

Global scope — The default environment where your code is executed for the first time.

Function scope — Whenever the flow of execution enters a function body.

Block scope — Whenever the flow of execution enters a block {}


Execution context is the environment / scope where the current code is being evaluated.


Closure

JS functions create a special bundle of values called Closure. It is created during the function declaration.

Closure contains all of the variables that are in the surrounding ( lexical ) scope at the time when the function is created. The closure contains the variables that were in the surrounding scope at the time when the function was created.

Closures are commonly used to enable data encapsulation, currying or module pattern.

To simplify we can say that Closure is the backpack with the variables that were in scope (around, outside the function) when the function was created.

NOTE:

When a function is created, it contains a function definition and a closure. The closure is a collection of all of the variables in the surrounding (lexical) scope at the time of the creation of the function.

We can think of a Closure as a "backpack" that every function has, where it stores the variables that were surrounding it during the time when it was declared.


Example 1
function outer () {
  let secret1  = 'Shhhhh ';
  let secret2 = 'keep silent';

  return function () {
    return secret1 + secret2; // `secret1` and `secret2` are saved in the function's Closure
  }  
}


let inner = outer();

console.log(inner());

Example 2
function createMultiplier(multiplyBy) {
// var multiplyBy
  return function (number) {
  // debugger;
    return number * multiplyBy; // multiplyBy is saved in the Closure
  }
}

let timesTwo = createMultiplier(2);

console.log(timesTwo(5));
console.log(timesTwo(10));

Example 3
function createSecret (passwordString, mySecret) {
  let password = passwordString;
  let secret = mySecret;

  return function (passwordInput) {
    if (passwordInput === password) {
      console.log(`The Secret is : ${secret}`)
    }
    else {
      console.log('Wrong');
    }
  }
}


let getSecret = createSecret('bananarama', 'I sleep with my socks on!');

getSecret('mangotango'); // Wrong!

getSecret('bananarama')//  The secret is: ...

Encapsulation - Using constructors and closures

  class BankAccount {
    constructor(fName, lName, startBalance, defaultCurrency) {
      console.log('in constructor -> this: ', this);
      this.fName = fName;
      this.lName = lName;

      // We create methods on the instance (not on the prototype)
      // as each method will hold in Closure the reference to particular value of each account
      this.getBalance = function () {   // This function serves as a getter
        return startBalance + ' ' + defaultCurrency;
      }

      this.depositFunds = function (amount = 0) { // This function serves as a setter
        startBalance += amount;
      }

    }


  }

IIFE - Immediately invoked function expression

(function () {
    console.log(`Hello, I'm an IIFE.`)
})();

IIFE - with a passed argument

var name = 'Bob';


(function (user) {
  var name = user.toUpperCase();

  console.log(`Hello ${name}, I am an IIFE`);
})("sarah")


console.log(name);

BONUS - Module pattern

Module pattern enables the encapsulation of code (without a constructor).

const bankAccount1 = (function () {
  const accountNumber = 'ES-127-0000145670-8832';
  const transactions = [];
  
  let total = 0;
  
  /* `module` is an object representing a bank account with the data
  		encapsulated in the closure of each method.
 */
  const module = {
    
    balance: function () {
      return `${total}€`;
    },
    
    transactionCount: function () {
      return transactions.length;
    },
    
    lastTransaction: function() {
      return transactions[transactions.length - 1];
    },
    
    transaction: function (amount) {
      total = total + amount;
      const type = amount < 0 ? 'WITHDRAW' : 'DEPOSIT';
      
      const newTransaction = { amount: amount , date: new Date() } ;
      transactions.push(newTransaction);
      
      console.log(`${type} - Transaction successful: ${amount} €`);
    }
    
  }
  
  return module; // return the newly created object
})();


// DEPOSIT
bankAccount1.transaction(500);

// WITHDRAW
bankAccount1.transaction(-495);

// GET BALANCE
bankAccount1.balance();

// GET NUMBER OF TRANSACTION
bankAccount1.transactionCount();

// TRY CHANGING THE ACCOUNT `total` DIRECTLY
//   :) - it is not accessible as it is in the closure 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment