Categories
JavaScript Best Practices

Why it’s Time to Stop Using JavaScript IIFEs

Spread the love

In JavaScript speak, IIFE stands for Immediately Invoked Function Expressions.

It’s a function that’s defined and then executed immediately.

In this article, we’ll look at why it’s time to stop writing IIFEs in our code.

We Can Define Block-Scoped Variables in JavaScript

Since ES6 is released as a standard, we can declare block-scoped variables and constants with let and const. It also introduced stand-alone blocks to isolate variables and constants into their own blocks, unavailable to the outside.

For example, we can write:

{
  let x = 1;
}

Then x wouldn’t be available to the outside.

It’s much cleaner than:

(()=>{
  let x = 1;
})();

Now that ES6 is supported in almost all modern browsers, we should stop using IIFEs to separate variables from the outside world.

Another way to isolate variables are modules, which are also widely supported. As long as we don’t export them, they won’t be available to other modules.

We Don’t Need Closures As Much Anymore

Closures are functions that return another function. The returned function may run code that’s outside of it but inside the enclosing function.

For example, it may commit some side effects as follows:

const id = (() => {
  let count = 0;
  return () => {
    ++count;
    return `id_${count}`;
  };
})();

Again, this is more complex and unnecessary now that we have blocks and modules to isolate data.

We can just put all that in their own module, then we won’t have to worry about exposing data.

It also commits side effects, which isn’t good since we should avoid committing side effects whenever possible. This is because they make functions hard to test as they aren’t pure.

Functions that return functions also introduce nesting when we can avoid it and so it’s more confusing than ones that don’t.

The better alternative is to replace it with a module.

With a module, we can write:

let count = 0;

export const id = () => {
  ++this.count;
  return `id_${count}`
}

In the code above, we have the same count declaration and we export the id function so that it can be available to other modules.

This hides count and exposes the function we want like the IIFE, but there’s less nesting and we don’t have to define another function and run it.

Aliasing Variables

Again, we used to write something like this:

window.$ = function foo() {
  // ...
};

(function($) {
  // ...
})(jQuery);

Now we definitely shouldn’t write IIFEs just to create aliases for variables since we can use modules to do this.

With modules, we can import something with a different name.

Today’s way to do that would be to write:

import { $ as jQuery } from "jquery";

const $ = () => {};

Also, we shouldn’t attach new properties to the window object since this pollutes the global scope.

Capturing the Global Object

With globalThis , we don’t have to worry about the name of the global object in different environments since it’s becoming a standard.

Therefore, we don’t need an IIFE to capture the global object by writing the following in the top-level:

(function(global) {
  // ...
})(this);

Even before globalThis , it’s not too hard to set the global object by writing:

const globalObj = self || window || global;

Or if we want to be more precise, we can write:

const getGlobal = () => {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};

Then we don’t have to add the extra function call and nesting introduced by the IIFE.

Optimization for Minification

With JavaScript modules, we don’t have to segregate code with IIFEs any more so that our files can minify properly.

Webpack, Browserify, Parcel, Rollup, etc., can all deal with modules properly, so we should use them instead to create much cleaner code.

Conclusion

It’s time to stop writing IIFEs in our code. It adds extra function definitions and nesting.

Also, it’s an anachronism from the times before JavaScript modules are introduced and widely used. In 2020, we should use modules and blocks for segregating code.

Block scoped variables are used for keeping variables from being accessible from the outside within a module.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *