4
function Foo() {
    var that = this;
    that.bar = function() {}
    that.baz = function() {}

    (function() {
        that.baz();
    }());
}
new Foo;

Uncaught TypeError: Object #<Foo> has no method 'baz'

that.bar works fine, it's only the last function that doesn't exist. Adding a ; after the baz function definition fixes everything.

I know excluding ; can mess some things up, but I thought for sure you're not supposed to put ; after functions. No language does that. Why does excluding the ; after the baz function cause this to break? Should I be putting ; after my function definitions?

Farzher
  • 11,368
  • 15
  • 57
  • 93
  • 1
    Note: `that.baz` is not a function, it is "property" of `that`. Most semicolon languages require `;` in such cases... – Alexei Levenkov May 14 '13 at 01:46
  • It's nicer to use `!function() {...}()` for immediately invoking functions, since it avoids such issues. That said, one rule to keep in mind, if you ever start a new line with `(` or `[`, just put a semicolon before it... `;(...` `;[...` –  May 14 '13 at 02:40

3 Answers3

10

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:

  1. First, function() {} is evaluated (not executed), yielding a function object.
  2. This function object is then called, but wait! It has some arguments we have to evaluate before we can begin executing it proper.
  3. The only argument is the expression

    function() {
        that.baz();
    }()
    

    That's obviously an immediately-invoked function expression, so we'll begin executing it.

  4. 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.
  5. We never returned anything from function() { that.baz(); }, so the argument to function() {} is undefined.
  6. 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.
  7. 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.

Community
  • 1
  • 1
icktoofay
  • 117,602
  • 18
  • 233
  • 223
  • Thank you for the very interesting discussion! I want to read it thoroughly and update my answer to make any needed corrections, but I'm out of time for tonight. Will take a closer look tomorrow, and in the meantime you get a grateful upvote. – Michael Geary May 14 '13 at 03:05
5

You don't need a semicolon after a function declaration:

function foo() {
}

There is no harm done if you do add a semicolon, you just don't need it:

function foo() {
};

That inserts an empty statement after your function declaration, which has no effect. In the same way, this would do no harm, other than confusing people who read your code:

var i = 1;;;;;;;;;;;;;;;;;;;;;;;;;;

OTOH, you do need a semicolon after an assignment statement:

that.prop = 1;

and that doesn't change if the value you're assigning is a function expression:

that.foo = function() {};
Michael Geary
  • 26,814
  • 8
  • 56
  • 71
  • 2
    No you do not **need** a semicolon after an assignment statement. They're **needed** in very few cases. http://mislav.uniqpath.com/2010/05/semicolons/ . Consistency should be encouraged though – Ian May 14 '13 at 01:57
  • Thank you. This was less obvious when my functions contained code and spanned multiple lines. putting `;` at the end looks wrong. Maybe I'll use the other type of function definition. – Farzher May 14 '13 at 01:58
  • @StephenSarcsamKamenar As for different definitions for functions - http://stackoverflow.com/questions/336859/javascript-var-functionname-function-vs-function-functionname – Ian May 14 '13 at 01:59
  • @Ian - that's an excellent point. JavaScript will insert semicolons for you in many cases. OTOH, the grammar for [a function declaration does not require a semicolon at all](http://stackoverflow.com/questions/1834642/best-practice-for-semicolon-after-every-function-in-javascript) - JavaScript doesn't even bother inserting one, so it's not even a question of automatic semicolon insertion. It does insert one for you when you leave it out in the other cases, so it really is a bit of a different situation. – Michael Geary May 14 '13 at 02:18
0

This will also work

function Foo() {
    var that = this;
    { that.bar = function() {} }
    { that.baz = function() {} }

    (function() {
        that.baz();
    }());
}
new Foo;

Get it?

They are two statements. They require two endings.

Lodewijk Bogaards
  • 18,519
  • 2
  • 25
  • 49