最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Javascript undefined class function when missing semicolon - Stack Overflow

programmeradmin1浏览0评论
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?

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?

Share asked May 14, 2013 at 1:42 FarzherFarzher 14.6k21 gold badges75 silver badges106 bronze badges 2
  • 1 Note: that.baz is not a function, it is "property" of that. Most semicolon languages require ; in such cases... – Alexei Levenkov Commented May 14, 2013 at 1: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... ;(... ;[... – user1106925 Commented May 14, 2013 at 2:40
Add a ment  | 

3 Answers 3

Reset to default 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, posing 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 e at the start of an expression, so JavaScript starts parsing an expression. Only an expression can e 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.

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() {};

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.

发布评论

评论列表(0)

  1. 暂无评论