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?
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.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
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 ofconsole.log
, you might get different errors.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
i.e. all as one statement.
Now parse the statement:
some
is called on the value ofconsole.log([a,b].length)[a,b]
the value of
console.log([a,b].length)[a,b]
is computed by taking the returned value ofconsole.log([a,b].length)
(undefined
) and then trying to access the property with the name of the value ofa,b
.a,b
evaluates to the value ofb
(try it in your console). There's no property with the value ofb
ofundefined
, so the resulting value will beundefined
as well.There's no method
some
onundefined
, hence the error.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:
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 propertyb
of that string, which is undefined, and the call toundefined.some()
fails.It's interesting to note that
str[a,b]
will resolve tostr[b]
assuming str is a string. As Kamil points out,a,b
is a valid Javascript expression, and the result of that expression is simplyb
.