Tutorial

Understanding JavaScript Closures: A Practical Approach

Draft updated on Invalid Date
Default avatar

By Paul Upendo

Understanding JavaScript Closures: A Practical Approach

This tutorial is out of date and no longer maintained.

Introduction

Learning a new language involves a series of steps, whereas its mastery is a product of patience, practice, mistakes, and experience.

Some developers will have enough knowledge to deliver on features as per a client’s demand, but it takes more than just that to be a good developer.

A good developer is one who takes time to go back and get a good grasp of a language’s underlying/core concepts.

Today we take a deeper look at JavaScript closures and hope that the knowledge you learn will be beneficial in your projects.

What is a JavaScript closure?

A JavaScript Closure is when an inner function has access to members of the outer function (lexical scope) even when executing outside the scope of the outer function.

Therefore, we cannot afford to talk about closure while leaving out functions and scope.

Scope in JavaScript

Scope refers to the extent of visibility of a variable defined in a program. Ways to create scope in JavaScript are through: try-catch blocks, functions, the let keyword with curly braces among others. We mainly have two variations of scope: the global scope and local scope.

var initialBalance = 0 // Global Scope

function deposit (amount) {
  /**
   * Local Scope
   * Code here has access to anything declared in the global scope
   */
  var newBalance = parseInt(initialBalance) + parseInt(amount)
  return newBalance
}

Each function in JavaScript creates its own local scope when declared.

This means that whatever is declared inside the function’s local scope is not accessible from the outside. Consider the illustration below:

var initialBalance = 300 // Variable declared in the Global Scope

function withdraw (amount) {
  var balance // Variable declared in function scope

  balance = parseInt(initialBalance) - parseInt(amount)
  return balance
}
console.log(initialBalance) // Will output initialBalance value as it is declared in the global scope
console.log(balance) // ReferenceError: Can't find variable: balance

Lexical Scope

JavaScript’s Lexical Scope is determined during the compile phase. It sets the scope of a variable so that it may only be called/referenced from within the block of code in which it is defined.

A function declared inside a surrounding function block has access to variables in the surrounding function’s lexical scope.

var initialBalance = 300 // Global Scope

function withdraw (amount) {
  /**
   * Local Scope
   * Code here has access to anything declared in the global scope
   */
  var balance = parseInt(initialBalance) - parseInt(amount)

  const actualBalance = (function () {
    const TRANSACTIONCOST = 35
    return balance - TRANSACTIONCOST /**
     * Accesses balance variable from the lexical scope
     */
  })() // Immediately Invoked Function expression. IIFE

  // console.log(TRANSACTIONCOST) // ReferenceError: Can't find variable: TRANSACTIONCOST
  return actualBalance
}

Invoking an inner function outside of its enclosing function and yet maintain access to variables in its enclosing function (lexical scope) creates a JavaScript Closure.

function person () {
  var name = 'Paul'  // Local variable

  var actions = {
    speak: function () {
    //  new function scope
      console.log('My name is ', name) /**
      * Accessing the name variable from the outer function scope (lexical scope)
      */
    }
  } // actions object with a function

  return actions /**
  * We return the actions object
  * We then can invoke the speak function outside this scope
  */
}

person().speak() // Inner function invoked outside its lexical Scope

A Closure allows us to expose a public interface while at the same time hiding and preserving execution context from the outside scope.

Some JavaScript design patterns make use of closures.

Module Pattern

One of these well-implemented patterns is the module pattern, this pattern allows you to emulate: private, public, and privileged members.

var Module = (function () {
  var foo = 'foo' // Private Property

  function addToFoo (bam) { // Private Method
    foo = bam
    return foo
  }

  var publicInterface = {
    bar: function () { // Public Method
      return 'bar'
    },
    bam: function () { // Public Method
      return addToFoo('bam') // Invoking the private method
    }
  }

  return publicInterface // Object will contain public methods
})()

Module.bar() // bar
Module.bam() // bam

From our module pattern illustration above, only public methods and properties in the return object will be available outside the closure’s execution context.

All private members will still exist as their execution context is preserved but hidden from the outside scope.

More illustrations on Closures

When we pass a function into a setTimeout or any kind of callback. The function still remembers the lexical scope because of the closure.

function foo () {
  var bar = 'bar'
  setTimeout(function () {
    console.log(bar)
  }, 1000)
}

foo() // bar

Closure and loops

for (var i = 1; i <= 5; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i)
    }, i * 1000)
  })(i)
}
/**
* Prints 1 through 5 after each second
* Closure enables us to remember the variable i
* An IIFE to pass in a new value of the variable i for each iteration
* IIFE (Immediately Invoked Function expression)
*/
for (let i = 1; i <= 5; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i)
    }, i * 1000)
  })(i)
}
/**
* Prints 1 through 5 after each second
* Closure enabling us to remember the variable i
* The let keyword rebinds the value of i for each iteration
*/

Conclusion

I bet we now have an understanding of closures and can do the following:

  • Illustrate its use cases or identify it in contexts we never knew we used it
  • Preserve execution context as we wish
  • Implement code in JavaScript’s module pattern
  • Use closures in our code, with a clear understanding

Until next time, happy coding.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Paul Upendo

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel