DEV Community

Abdullah Al Numan
Abdullah Al Numan

Posted on • Updated on

Explain why the following doesn't work as an IIFE: "function foo(){ }();". What needs to be changed to properly make it an IIFE?

This code returns a token error:

function foo(){ }(); // Error: Unexpected token ')'
Enter fullscreen mode Exit fullscreen mode

Parens

If we place an expression inside the second parens (the grouping operator, which expects an expression to be evaluated), the error goes away.

function foo(){ }(1);
Enter fullscreen mode Exit fullscreen mode

So, we know the token error is due to the second parens, which did not have any expression to evaluate.

But... it still doesn't work as an IIFE.

Breakdown

Let's change foo() to log a greeting. As you can see, nothing gets logged to the console.

function foo(){ console.log('Hello from foo!') }(1); // Nothing logged to the console
Enter fullscreen mode Exit fullscreen mode

This is because foo() is never invoked.

In fact, we are wrong in expecting foo() to be invoked like this:

function foo(){ console.log('Hello from foo!') }();
Enter fullscreen mode Exit fullscreen mode

Because, the second parens does not stand for invoking foo() here. And that is because the function declaration left to it, function foo(){ }, is not an expression. It's just a definition.

The parser sees the code above as:

function foo(){ console.log('Hello from foo!') };
();
Enter fullscreen mode Exit fullscreen mode

Fix

In order to make the second parens (immediately) invoke foo(), we need to make the function declaration evaluate to a function expression first. And guess what, we do it with another parens.

(function foo(){ console.log('Hello from foo!') });
Enter fullscreen mode Exit fullscreen mode

We can then go ahead and apply the invocation parens:

(function foo(){ console.log('Hello from foo!') }(); // "Hello from foo!"
Enter fullscreen mode Exit fullscreen mode

Another fix would be to wrap the entire code in an overarching parens. This will also make it work as an IIFE:

(function foo(){ console.log('Hello from foo!') }()); // "Hello from foo!"
Enter fullscreen mode Exit fullscreen mode

Here, everything, including the last parens is considered part of one expression and so foo() gets invoked.


References

  1. Immediately-Invoked Function Expression (IIFE)
  2. IIFE

Top comments (7)

Collapse
 
tracygjg profile image
Tracy Gilmore

Abdullah, Greetings. Many of your code examples above are missing the closing parenthesis on the console.log.

Collapse
 
anewman15 profile image
Abdullah Al Numan

Aha, yes!

Collapse
 
armousness profile image
Sean Williams

As I understand JavaScript, you basically shouldn't name this function. Normally function foo() { console.log("hello world"); } creates a declaration, that is, it binds the function to the name foo. However, I believe this immediate invocation business has the effect of not creating the binding, or at least, of immediately discarding the binding.

If I'm right about this, it doesn't have any real negative consequences, except that you might expect to be able to invoke foo later and might find the error confusing.

Collapse
 
peerreynders profile image
peerreynders

However, I believe this immediate invocation business has the effect of not creating the binding, or at least, of immediately discarding the binding.

A function declaration cannot be immediately invoked.

An IIFE is always a function expression (hence the name) so the function name is only bound and visible to the function object itself (and can be seen in the debugger IIRC).

 
armousness profile image
Sean Williams

True, recursion is the reason to do it, though none of the examples here were recursive. You actually can do anonymous recursion, if you're willing to invoke the dark arts:

Y = f => (x => x(x))(x => f(y => x(x)(y)))

Y(f => n => n === 0 ? 1 : n * f(n - 1))(5)
Enter fullscreen mode Exit fullscreen mode

Yeah this still introduces a name Y, but it's reusable so you actually do want that. You could get around that—after all, Church's original lambda calculus didn't have declarations—it's just an even worse idea than using the Y-combinator.

Collapse
 
j3ffjessie profile image
J3ffJessie

I have to give kudos immediately after reading and thank you for including the references to IIFE so that people can learn what that Acronym stands for. Very well put together article.

Collapse
 
peerreynders profile image
peerreynders • Edited
(function foo() {
  console.log('Hello from foo!');
})();
Enter fullscreen mode Exit fullscreen mode

Way back in 2011 Douglas Crockford had some interesting things to say about the stylistic convention that Prettier (and the ESLint default) nowadays enforces on IIFEs.

Once you hear it, you'll never be able to unsee it—you've been warned.