4

My understanding of the concept is that iifes allow you to simulate a "private" scope which prevents the global scope from becoming cluttered.

Now that ECMAScript 6 has seen fairly widespread implementation and given us access to block level scoping via const and let, is any other reason to use the iife pattern? (beyond the need to provide backward compatibility..)

dayna j
  • 71
  • 1
  • 6
  • This is mostly an opinion based question but I'd imagine they'll still be relevant for a while. I think Immediately invoked async and generator functions are relevant IMO. – MinusFour Aug 24 '18 at 01:46
  • one advantage of a function is being able to use `return` to halt execution early. you can also alias surrounding lexical variables with parameters+arguments. Functions can also name the program to use for debugging. Lastly, you can recurse/restart a named IIFE from within, whereas there's no reference to blocks. – dandavis Aug 24 '18 at 02:35
  • @dandavis - All of those benefits can also be achieved by just using a named function which is how most languages handle this. Perhaps there is a usefulness to sometimes being inline for accessing parent scoped variables. – jfriend00 Aug 24 '18 at 02:54
  • 1
    Most uses for IIFE's have disappeared because block scoped variables solve the most common thing that IIFE's are used for. That said, there are still some less common reasons to occasionally use one (though I haven't encountered one of those in my code yet). – jfriend00 Aug 24 '18 at 02:56

2 Answers2

4

One of the benefits to an IIFE that a plain block cannot achieve is the ability for its output to be assigned to a const. For example:

const output = (() => {
  // some calculations that take a few lines
  const now = new Date();
  return now.getMinutes() + ':' + now.getSeconds();
})();
console.log(output);

Let's say we wanted to enclose the now variable in an IIFE because it's only relevant for calculating output, and is not needed anywhere else. If one were to use a plain block for this purpose instead, it would only be possible if output was declared with let rather than const:

let output;
{
  // some calculations that take a few lines
  const now = new Date();
  output = now.getMinutes() + ':' + now.getSeconds();
}
console.log(output);

Because const significantly improves the readability of code (in my opinion), this arguably a potential reason to prefer the IIFE over the plain block, though of course code styles differ.

That said, if one were to extract till you drop, some might say to avoid IIFEs and declare a standalone function, which can then be imported and called:

function getTimeStr() {
  const now = new Date();
  return now.getMinutes() + ':' + now.getSeconds();
}

// another file:
// import getTimeStr from './getTimeStr';
const output = getTimeStr();
console.log(output);

As in the above snippet, the use of a modern code bundler such as Webpack (or, perhaps, ES6 modules) may have more of an impact on less usage of IIFEs than than the ES6 ability to use plain blocks. It depends on the coding style one prefers, but using IIFEs is certainly a possible option one might choose.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • 1
    I don't get your first point. `const now = new Date(); const output = now.getMinutes() + ':' + now.getSeconds();`. Sure, `now` is not needed elsewhere, but who cares? It's `const` and can't be mucked with anyway. This does not seem like a rationale for an IIFE in any common or useful way. Why complicate the code to hide one temporary variable? – jfriend00 Aug 24 '18 at 02:50
  • It's just an example. For just one variable and two lines, yes, it does look pretty silly, but if the calculations needed to determine the output were more involved and there were more intermediate variables needed to come up with the final `output`, enclosing all of that calculation in one function (perhaps in an IIFE) would make more sense. This would be especially true if the function/block the whole code is contained in is *long* (though, of course, better to avoid long functions in the first place, if possible). Fewer variables in scope can mean (slightly) less cognitive overhead. – CertainPerformance Aug 24 '18 at 03:06
  • The point was just that it's a *possible option* one might choose. Still, I'd prefer to extract into a named function in most cases – CertainPerformance Aug 24 '18 at 03:06
  • 1
    The example as you show it cries out to be made into a reusable named function. It might be possible to come up with a more complicated example with lots of intermediate variables and lots of access to parent scoped variables that is not so easy to make into a named function. But, at that point, the code probably just needs to be simplified in other ways and still probably an IIFE isn't the right solution. – jfriend00 Aug 24 '18 at 03:08
  • If you are using an IIFE to create a value that persist the way you have it, why not just create a closure..? – Phil Jul 29 '21 at 20:25
1

Pretty much all the common reasons for using an IIFE in my code have been replaced with block-scoped variables.

The ONE use I have seen in real code is to simulate top-level await:

(async function() {
    try {
        const data1 = await something1();
        const data2 = await something2(data1);
        ...
     } catch(e) {
        ...
     }
})();

The remaining reasons I see for an IIFE are when you want the advantages of a function such as using return or throw to stop execution of a defined portion of the remaining code (without nesting in an if) or when you want to call something recursively. But, of course, you could get all of those advantages with a locally scoped named function too.

But, if the code in your function benefits heavily from accessing parent scoped variables, then it really wants to be inline and that creates a possible reason for an IIFE to combine the advantages of being in a separate function with inline access to parent scope.

Now, in reality, most/all of these cases can also be solved with a little refactoring to either define the named function in the local scope (so it still has access to parent scoped variables) or just pass things from the parent scope to a named function so it has access to them that way and, in fact, that's what we all do in other languages. The only cost to that is one more local function name (which can even be locally scoped if you want to hide it).

jfriend00
  • 683,504
  • 96
  • 985
  • 979