Contrary to Michael Geary's answer, assignment statements do not require semicolons.
Consider the humble immediately-invoked function expression:
(function() { /* do something */ })();
What are the extra parentheses wrapping the function() { ... }
for? They're to prevent it from being interpreted as a function definition, forcing it to be interpreted as a function expression. It's basically equivalent to this, but without creating another variable:
var temp = function() { /* do something */ };
temp();
But parentheses are only one way to introduce something that must be an expression. In particular, an initializer of an assignment statement will introduce an expression1:
someVar = /* what goes here must be an expression */;
Clearly, composing these concepts shows that in this:
someVar = (function() { return 5; })();
The wrapping parentheses are not actually needed! We could just as easily write this because the function()
cannot be interpreted as a function definition:
someVar = function() { return 5; } ();
So what does this have to do with semicolons? It turns out that if the next line can continue the statement or expression, it will.2 You've written this:
that.baz = function() {}
(function() {
that.baz();
}());
With this knowledge, you know it's actually being interpreted as this:
that.baz = (function() {})(function() {
that.baz();
}());
That's a bit tricky, so here's what's happening:
- First,
function() {}
is evaluated (not executed), yielding a function object.
- This function object is then called, but wait! It has some arguments we have to evaluate before we can begin executing it proper.
The only argument is the expression
function() {
that.baz();
}()
That's obviously an immediately-invoked function expression, so we'll begin executing it.
- We try to find
that.baz
and call it, but it fails because that.baz
does not exist and thus resolves to undefined
! Oh no! The JavaScript engine stops here because an uncaught exception was thrown, but for clarity, let's pretend it went on.
- We never returned anything from
function() { that.baz(); }
, so the argument to function() {}
is undefined
.
- We now execute
function() {}
proper. It never binds its argument, so the undefined
is ignored. It doesn't return anything, so the call results in undefined
.
- Now we're done evaluating that expression! We'll set
that.baz
to the result, undefined
.
Hopefully you see now that the parser misinterpreted your immediately-invoked function expression as, well, an argument to another anonymous function, immediately invoking it.
How to prevent it?
As mentioned previously, parentheses are not the only way to unambiguously introduce an expression. In fact, when they're in the middle of the expression unintentionally, it can cause problems like this. We also mentioned assignments, but we don't want to assign something.
What's left? Unary operators. There's a few we can use. I like !
, so we'll use that. Rather than using:
(function() {
that.baz();
}());
We use:
!function() {
that.baz();
}();
The !
can only come at the start of an expression, so JavaScript starts parsing an expression. Only an expression can come after !
, so the function is parsed as a function expression rather than a function definition. Because of precedence, the function is called first. It doesn't return anything, so it returns undefined
. undefined
is falsy, so !
flips it and yields true
. Since we never did anything with the result of this expression, the true
is discarded. (It wouldn't have mattered if we returned a truthy value either; then !
would have yielded false
and that would be discarded. No matter what, it'll be discarded.)
TLDR: Use a unary operator rather than parentheses for IIFEs.
Footnotes
1 Technically, assignment is an expression, not a statement, but for our purposes it does not matter.
2 With the exception of return
, continue
, break
, and similar statements where it would confuse much more confusion.