No semicolon before [] is causing error in JavaScr

2020-01-25 10:59发布

问题:

var a = [1, 2, 3, 4];
var b = [10, 20, 30, 40];
console.log([a, b].length)[a, b].some(function(x) {
  x.push(x.shift())
});

I was extremely surprised today when this code caused

[a,b].some(function(x){ x.push(x.shift()) });
      ^
TypeError: Cannot call method 'some' of undefined

Obviously the JavaScript 'auto semicolon insertion' is not working as expected here. But why?

I know you might recommend to use ; everywhere to avoid something like that, but the question is not about whether it is better to use ; or not. I would love to know what exactly happens here?

回答1:

When I'm worried about semicolon insertion, I think about what the lines in question would look like without any whitespace between them. In your case, that would be:

console.log([a,b].length)[a,b].some(function(x){ etc });

Here you're telling the Javascript engine to call console.log with the length of [a,b], then to look at index [a,b] of the result of that call.

console.log returns a string, so your code will attempt to find property b of that string, which is undefined, and the call to undefined.some() fails.

It's interesting to note that str[a,b] will resolve to str[b] assuming str is a string. As Kamil points out, a,b is a valid Javascript expression, and the result of that expression is simply b.



回答2:

In general, one could say that implicit semi-colon's can easily fail when defining an array on a new line, because an array defined on a new line is interpreted as a property access of the value of the expression on the previous line.

Javascript does only consider new lines to mark the end of a statement if not ending the statement after this new line would cause a parse error. See What are the rules for JavaScript's automatic semicolon insertion (ASI)? and EcmaScript 5 spec for the exact rules. (Thanks to Rob W and limelights)

What happens is the following:

The code get interpreted as

console.log([a,b].length)[a,b].some(function(x){ x.push(x.shift()) });

i.e. all as one statement.

Now parse the statement:

some is called on the value of console.log([a,b].length)[a,b]

the value of console.log([a,b].length)[a,b] is computed by taking the returned value of console.log([a,b].length) (undefined) and then trying to access the property with the name of the value of a,b. a,b evaluates to the value of b (try it in your console). There's no property with the value of b of undefined, so the resulting value will be undefined as well.

There's no method some on undefined, hence the error.



回答3:

JavaScript doesn't treat every line break as a semicolon. It usually treats line breaks as semicolons only if it can’t parse the code without the semicolons. Basically, JavaScript treats a line break as a semicolon if the next non-space character cannot be interpreted as a continuation of the current statement. JavaScript - The Definitive Guide: 6th Ed. section 2.4

So, in your case, it is interpreting the line as something like

console.log([a,b].length)[a,b].some(function(x){ x.push(x.shift()) });

And that is the reason for error. JavaScript is trying to perform array-access on the results of console.log([a,b].length). Depending on the JavaScript engine and the return value of console.log, you might get different errors.



回答4:

If it is the last statement of the function or flow, you can avoid ';' but it is recommended to put ';' at the end of the each statement to avoid such error.