-->

Why is a semicolon required at end of line?

2019-02-16 17:02发布

问题:

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.

回答1:

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( () )
)


回答2:

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:

Because

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

is a valid JavaScript expression. And if some adjacent lines can be stick together to form a valid JavaScript expression, your JavaScript engine will stick them together.

Though it is a valid JavaScript expression, a.push returns a number, which is not a function, and when you try to call something that is not a function, it returns the error you see.



回答4:

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.



回答5:

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.



回答6:

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.



回答7:

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.