How JavaScript Closures and IIFEs Work

How JavaScript Closures and IIFEs Work

ship-in-a-bottle-92542_1280.jpg

For the first 100 days of 2021, as part of #100DaysOfCode, I am re-introducing myself to common JavaScript concepts and blogging about them! For this post, I am going to talk about closures and IIFEs and how important they can be in JavaScript programming.

Closures

In JavaScript, functions are considered to be "first-class" because, like other variables, they can be assigned as a value, passed as an argument and even returned from other functions! Because JS functions are regarded this way, each time they are created, they come with an ability to utilize variables from outside its scope. This ability is known as a closure.

Closures refer to an enclosed function's access to its surrounding state, the lexical scope. For example, in this short code snippet:

let A = "Hello, World!"
function greeting() {
  console.log(A) 
}

greeting() // --> Hello, World!

Despite A not being defined outside the scope of greeting(), it is still accessible within the function. Let's see what happens if we change the value of A below the definition of the greeting() function:

let A = "Hello, World!"
function greeting() {
  console.log(A) 
}

A = "Hello, Other World!"

greeting() // --> Hello, Other World!

Normally, this isn't possible in other languages. But in JavaScript, thanks to closures, when greeting() is executed, the A variable within its scope is bound to the most up-to-date version of the global A variable. Thus, "Hello, Other World!" is logged to the console.

But the most classic example of using JS closures is when you invoke a function inside another function. Let's look at another example:

function FuncA() {
  let name = "Brandon" 
  function FuncB() {
    console.log(name)
  }
  FuncB() 
}

FuncA() // --> Brandon

Again, closures make this code functional (pun intended) by allowing enclosed inner functions to have access to everything inside its surrounding environment.

If you were to return FuncB instead of just invoking it, the name information is still there because of closures. We can execute either with additional variable assignment:

function FuncA() {
  let name = "Brandon" 
  function FuncB() {
    console.log(name)
  }
  return FuncB
}

let newFunc = FuncA()
newFunc() // --> Brandon

...or double parentheses:


function FuncA() {
  let name = "Brandon" 
  function FuncB() {
    console.log(name)
  }
  return FuncB
}

FuncA()() // --> Brandon

Since we're getting into more parentheses...

IIFEs

IIFE (or "iffy") stands for immediately invoked function expression. When you run JavaScript code, function statements go through a "creation" phase where they're prepared for execution but aren't run yet. This happens in the "execution" phase. But sometimes, we don't want a function to hang around and wait to be executed; we want it to run right as soon as it's created. Enter IIFEs.

(function(name) {
  console.log("Hello, " + name)
}("Brandon")) // --> Brandon

In the example above, we enclose an anonymous function statement in an additional set of parentheses. If we didn't do this, we would a SyntaxError thrown at us because function statement usually are expected to have a name. IIFEs get around that. IIFEs turn the function statement into an expression that is immediately invoked.

const AddTheSum = (function(a,b) {
  console.log(a + b) 
}(2,2))

// immediately invoked, no need to write AddTheSum to log to console

const Multiply = function(a,b) {
  console.log(a * b)
}

Multiply(2,3) // function expression stored but not executed; you must use Multiply() to invoke.

Conclusion

If you code with JavaScript, you've already been using closers. Even if one function is written in a file, it is still enclosed in the global scope. Closures let you link data defined in the global scope with the inner function that operate on that data.

There are many libraries and frameworks that wrap their entire code base in a function to avoid value collision in the global scope (IIFEs). This ensures that any duplicate variables with differing values belong in their respective contexts upon executing, preventing a collision.

Together, closures and IIFEs protect your JavaScript program and its data.

Resources

Closures (MDN docs)

IIFEs (MDN docs)

JavaScript Under the Hood Pt. 7: IIFEs