DEV Community

John Au-Yeung
John Au-Yeung

Posted on • Originally published at thewebdev.info on

Why it’s Time to Stop Using JavaScript IIFEs

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.

The post Why it’s Time to Stop Using JavaScript IIFEs appeared first on The Web Dev.

Oldest comments (3)

Collapse
 
pentacular profile image
pentacular • Edited

Closures are functions that return another function.

Closures are not functions that return another function.

Here x is a function which returns another function, but no lexical closure exists here.

const x = () => () => 1;

Here y is a function which does not return another function, but which has a lexical closure.

let i = 0;
const y = () => i;

i is a free variable in y and so it closes over the lexical binding of i in the surrounding scope, producing a lexical closure.

We can say that y is a function with a lexical closure over i.

(Saying that z is a closure is short-hand for this, which has lead to much confusion.)

Collapse
 
bernardbaker profile image
Bernard Baker

I can understand that you feel that IIFE should be phased out. But I remember years ago when using an IIFE gave portability of code and modular benefits. They still do today. Just not with the benefits of current language developments.

Rightly said that you feel people should stop using them. This is Dev.to. A place where ideas are the norm and conversation rule over judgement.

And the benefits of modules, blocks, patterns and dynamic imports are exciting.

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt • Edited

What do you think closures that return (possibly multiple) values?

Or, it is better to name the function and put the function at top level, anyways?

(IMO, naming is not that bad and may help debugging, but naming may clash, therefore, better block-scoped, if possible.)

ES6 blocks can seem to return values, but it might be a nice proposal?