Why is a semicolon required at end of line?
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 fi开发者_如何学Cgure out what would be causing this error.
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( () ) )
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.
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.
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.
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.
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
.
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.
精彩评论