Can You Find The Bug in This Code?

This is an actual bug I had once.

Here’s a bit of Javascript that prints “Hello World!” on two lines:

(function() {
  (function() {
    console.log('Hello')
  })()

  (function() {
    console.log('World!')
  })()
})()

…except it fails with a runtime error. Can you spot the bug without running the code?

Scroll down for a hint.









Hint

Here’s the text of the error:

TypeError: (intermediate value)(...) is not a function

What’s going on?

Scroll down for the solution.









Solution

One character fixes this code:

(function() {
  (function() {
    console.log('Hello')
  })();
  (function() {
    console.log('World!')
  })()
})()

Without that semicolon, the last function is interpreted as an argument to a function call. Here’s a rewrite that demonstrates what’s going on when the code is run without the semicolon:

const f1 = function() { console.log('Hello'); };
const f2 = function() { console.log('World!'); };

f1()(f2)();

There are 3 function invocations in that last line:

  • f1 is called with no arguments
  • The return value of f1() is called with f2 as its only argument
  • The return value of f1()(f2) is called with no arguments

Since the return value of f1() is not a function, the runtime throws a TypeError during the second invocation.

With the semicolon added, this becomes:

const f1 = function() { console.log('Hello'); };
const f2 = function() { console.log('World!'); };

f1();(f2)();

Which runs as expected.

Wait, you had this bug once?

Yup.

Why would you ever write code with so many Immediately Invoked Function Expressions (IIFE)?

It’s a long story, but I’ll make a post soon explaining how I wrote bad enough code to have this bug. Subscribe (no spam, for real) if you want to get notified when it’s published!

UPDATE: I followed through and made that post.

The Lesson

Always use semicolons. This specific case was a bit contrived, but something similar could happen to you. Here’s another Hello World program that fails for a related reason:

const a = 'Hello'
const b = 'World' + '!'
[a, b].forEach(s => console.log(s))

I’ll leave figuring this one out as an exercise for you.

Most Javascript style guides require semicolons, including Google’s, Airbnb’s, and jQuery’s. To summarize: always use semicolons.

Subscribe to know whenever I post new content. I don't spam!


This blog is open-source on Github.

At least this isn't a full screen popup

That would be more annoying. Anyways, if you like what you're reading, consider subscribing to my newsletter! I'll notify you when I publish new posts - no spam.