Why is a semicolon required at end of line?

2019-02-16 17:15发布

Why does this work:

a = []
a.push(['test']);
(function() {alert('poop')})()

But this gives the error "number is not a function":

a = []
a.push(['test'])
(function() {alert('poop')})()

The only difference is the semicolon at the end of line 2. I've been writing Javascript for a long time now. I know about automatic semicolon insertion, but I can't figure out what would be causing this error.

7条回答
The star\"
2楼-- · 2019-02-16 17:37

I'm not a Javascript expert (or even a newbie :), but if you combine the second and third lines, it still looks syntactically valid:

a.push(['test'])(function() {alert('poop')})()

That's trying to treat the result of a.push(['test']) as a function, passing a function into it... and then calling the result as a function as well.

I suspect that the semi-colon is required if the two statements can be syntactically combined into a single statement, but that's not what you want.

查看更多
来,给爷笑一个
3楼-- · 2019-02-16 17:40

a.push(['test']) will return the length of the array, a number. Without a semicolon following, the compiler will then interpret the opening parentheses of self-invoking function following as if you're trying to execute that number as a function with arguments. Let's say it returned a length of 7, then essentially what is happening here is as if you wrote:

7 (function() {alert('poop')})();

Hence the error "number is not a function" because it doesn't know how to invoke 7 as a function.

查看更多
来,给爷笑一个
4楼-- · 2019-02-16 17:41

a.push(['test']) returns a number.

You then attempt to call the number as a function with function() {alert('poop')} as the only argument.

Hence your error, number is not a function.

查看更多
小情绪 Triste *
5楼-- · 2019-02-16 17:45

Take a look at this example of chained function calls.

a.push(['test'])(function() {alert('poop')})()

Look familiar? This is how the compiler/interpreter views your code.

Detail

Here is a portion of the grammar used to describe call expressions.

CallExpression : 
	MemberExpression Arguments 
	CallExpression Arguments 
	CallExpression [ Expression ] 
	CallExpression . IdentifierName 

Essentially each group (...) is considered as Arguments to the original MemberExpression a.push.

a.push (['test'])                // MemberExpression Arguments 
(function() {alert('poop')})     // Arguments  
()                               // Arguments 

Or more formally

CallExpression(
    CallExpression(  
        CallExpression(
            MemberExpression( a.push ),
            Arguments( (['test']) )
        ),
        Arguments( (function() {alert('poop')}) )
    ),
    Arguments( () )
)
查看更多
戒情不戒烟
6楼-- · 2019-02-16 17:45

The result of a.push() is the size of the returned array, in this case, 1. The enclosing parentheses around your function make the Javascript parser think that you're trying to call:

1( function() {alert('poop')} )()

Or that you're trying to call a function called 1 with an anonymous function as a parameter, then execute the return as a function.

查看更多
The star\"
7楼-- · 2019-02-16 17:54

There are cases where white space is not required as a token separator but only used to improve readability:

White space characters are used to improve source text readability and to separate tokens (indivisible lexical units) from each other, but are otherwise insignificant. White space may occur between any two tokens […] but cannot appear within any other kind of token.

In this case the white space between a.push(['test']) and (function() {alert('poop')})() is not for token separator and thus insignificant. So it’s equivalent to this:

a.push(['test'])(function() {alert('poop')})()

And since a references an empty array with the length 0, calling a.push(['test']) appends one element to a and returns the updated value of a.length, i.e. 1:

1(function() {alert('poop')})()

And the rest is history.

查看更多
登录 后发表回答