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?
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
.
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.
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.
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.