No semicolon before [] is causing error in JavaScript - javascript

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.

Related

Semicolon before (()=>true)()

It works fine:
const foo = 1; // any number, string, bolean or object
(() => console.log('stuff'))()
But it doesn't work without semicolon:
const foo = 1 // TypeError: 1 is not a function
(() => console.log('stuff'))()
Hm...
Should not the call of an anonymous function be treated as a separate instruction in the case when the first bracket can not be interpreted as a correct continuation of the previous instruction?
Yes, but it's only about syntactically correct continuations.
1(() => console.log('stuff'))()
is a syntactically correct expression and parses as "call 1 with an argument of () => console.log('stuff'), then call the result of that without arguments". This throws an exception at runtime (1 is not function, so it can't be called), but it's still a valid expression.
You should alway use semicolons. If you do not add them, Javascript will guess where to insert them and will lead to errors.
In your case, it is interpreting that you are calling a function.
A good article on the topic on how semicolons are automatically inserted:
The norm: The parser treats every new token as part of the current
statement, unless there is a semicolon that terminates it. The
following examples show code where you might think a semicolon should
be inserted, but isn’t. This illustrates the risks of omitting
semicolons.
No ASI:
a = b + c
(d + e).print()
This does not trigger ASI, because the opening parenthesis could follow c in a function call. The above is thus
interpreted as:
a = b + c(d + e).print();
…when the first bracket can not be interpreted as a correct continuation of the previous instruction?
But it can - as you can see, the code parses just fine, and executes. That it will throw a runtime exception when no semicolon isn't inserted doesn't matter to the ASI, it cannot know while parsing.
in javascript, it doesn't matter how many spaces you have, it will just be treated as one space only.
in your second code snippet, it just actually equals:
const foo = 1 (() => console.log('stuff'))()
which means you invoke a function called '1' and pass '()=>console.log('stuff')' as an argument. but apparently 1 is not a function, so it throw an error, hope make sense to you

JS code misunderstood by Babel [duplicate]

This question already has answers here:
What are the rules for JavaScript's automatic semicolon insertion (ASI)?
(7 answers)
Closed 6 years ago.
What's so terribly wrong about the following code
var obj = {a:1}
[1,2,3].forEach(console.log)
that Babel produces this malfunctional concoction?
var obj = { a: 1 }[(1, 2, 3)].forEach(console.log);
for any of ES201[567] preset. Verified with https://babeljs.io/repl.
Yes, terminating the first line with semicolon helps. Is it still a dangerous practice to not use semicolons?
UPDATE: before downvoting on the grounds of "gosh of course you should use semicolons", please acknowledge the evolution of not using them, along with arrow functions and ever growing toybox of array/object prototype functions. All modern JS examples do that, and ES6 newbie gets confused easily.
The problem is eluded to in the code compiled by Babel: the two lines you have given it are interpreted as one.
Is it still a dangerous practice to not use semicolons?
In my opinion, no. White-space is collapsed in JavaScript meaning multiple lines can be interpreted as one, so there are a few cases in which they are necessary, but whether this means that using semi-colons is an absolute must is really down to personal preference. Some people feel very strongly that you should, and they may well be right...
Anyhow, as you discovered, one of these cases is ending a line with an object or array and starting the next with an array literal ([]). You can read about the remaining cases here.
If the code you are working on does not use semi-colons, for example if it uses the standard code style, you can..
..add a semi-colon to the end of the first line:
var obj = {a:1};
[1,2,3].forEach(console.log)
..add a semi-colon to the beginning of the second line:
var obj = {a:1}
;[1,2,3].forEach(console.log) // beginning of last line
..avoid having to use a semi-colon at all by assigning the array literal to a variable:
var obj = {a:1}
var arr = [1,2,3]
arr.forEach(console.log)
____
Side note...
The reason the contents of your array literal are wrapped in brackets is because Babel has interpreted your array literal as an attempt to access a property on the object literal defined in the previous line. Of course, you can only access one property at a time using the array[index] syntax, so Babel uses the comma operator to take a single value from (1, 2, 3), which will be 3.
Read about the comma operator on MDN.

Does javascript use some sort of implicit "noop" call?

This is probably JS 101 but...
Can someone with a better understanding of JS engines explain to me why string literals, integers and so forth are 'ignored' by JS or treated as otherwise valid code?
JS Hint does give 'unexpected expression' reports however the code remains valid and runs.
I've created the following pen to, hopefully, explain what I mean here.
http://codepen.io/billythekid/pen/zyGbi/
var span = document.getElementsByTagName('SPAN')[0];
// let's show what I'm trying to say here by way of an expanded example.
function foo()
{
var bar = "something";
}
foo(); // does nothing useful, but returns nothing either - valid and understandable;
function baz()
{
return "nothing"; // a string
}
function zoo()
{
return 250; // an integer
}
var a = baz(); // the variable holds the return value. The function is evaluated and the return value is assigned to the variable.
span.innerHTML += a+"<br>";
baz(); // produces no error despite the function returning a value. Why doesn't the JS engine see this as the evaluated string "something" and try to run the string as a JS command?
span.innerHTML += "this code has run, so the JS didn't break above. Why wasn't the returned string parsed and invalid?<br>";
"something"; // the string literal
span.innerHTML += "this code has run, so the JS didn't break above. So why not? How is a string literal valid JS? Why no errors?<br>";
var b = zoo();
span.innerHTML += b+"<br>";
zoo();// produces no error despite the function returning a value. Why doesn't the JS engine see this as the evaluated integer 250 and try to run the string as a JS command?
span.innerHTML += "this code has run, so the JS didn't break above. So why not? How is an evaluated integer valid JS? Why no errors?<br>";
250; // the integer literal
span.innerHTML += "this code has run, so the JS didn't break above. So why not? How is an integer literal valid JS? Why no errors?<br>";
eval(250); // the integer literal
span.innerHTML += "this code has run, so the JS didn't break above. So why not? How is an evaluated integer literal valid JS? Why no errors?<br>";
eval("something"); // the string literal
span.innerHTML += "this code broke, it can't run a string that's not been defined as a function or whatever.<br>";
// and, had the previous code not broken...
non_func(); // doesn't exist!
span.innerHTML += "this code doesn't run! So it'll error out with a call to a function/variable that doesn't exist but not eval'd code that isn't JS, such as a string, or even the literals of these objects!<br>";
// It appears that anythign not explicitly wrapped in an eval function is ignored by JS rather than throwing any errors. Why is this?
Simply running a string literal such as "foo"; as a line in the console seems to return itself.
Is the JS internally wrapping simple cases like these in some sort of 'noop' method or internally garbage-collecting such things or does it simply see the code as "run" once it's gone past it and has nothing more to do (such as assign values to a variable, or some other thing?
I got to thinking about this when using a setInterval() call, if I assign it's return value (well, it's ID identifier) to a var for using in a clearInterval later it's valid but it's also valid when we ignore the returned ID. The ID isn't "parsed" as JS.
Using strict mode seems to have no effect on this behaviour either.
Hopefully I've not made this more confusing than it needs to be. :oD
One big culprit behind your confusion is the C programming language. In it, many things that you think are statements, such as assignments, are actually expressions.
//This is valid C and Javascript code
x = (y += 1);
//We all have been bitten by this one once haven't we?
while(x = y){
}
and in order to let these statements be used in lines of their own, there is a rule in the language grammar that lets an expression followed by a semicolon to count as a statement
stmt :=
<if_then_else> |
<while loop> |
... |
<expression> ';'
The rule for evaluating these single-expression statements is that the expression is evaluated for side-effects and its value gets ignored.
Javascript (and lots of other languages) adopted lots of things from the C syntax, including this particular treatment of expressions. Using a string as a statement does nothing with its value (unless the string is "use strict" - then its a useful hack that does something in new browsers but nothing in the old ones). Using a function call as a statement runs it for side effects and ignores its return value. Some more stingy languages, such as Haskell or Go will complain if you ignore return values but C and Javascript will just throw them away.
-- In Haskell you get a compiler warning for ignoring return values
do
functionThatReturnsNothing()
a <- functionThatReturnsAValue()
_ <- functionThatReturnsAValue() -- unless you ignore it explicitly
It appears that anythign not explicitly wrapped in an eval function is ignored by JS rather than throwing any errors.
Thank god its that way! It would be crazy error prone if things got implicitly eval-ed like that and strings should never be run as code unless you are explicit about it!

why {key:'value'} is printing "value" in console

While I was testing saw a behavior of console(Chrome). if i type console.log({key:'value'}) is printing Object {key: "value"} which was expected by me. But when I directly typed {key:'value'} in console it printing 'value' only while I expected Object {key: "value"}.
Saw same behavior in IE10 and FF.
What is the reason behind this behavior?
It's because your object is instead being interpreted as a block statement with a single statement within consisting of a string literal preceded by a label.
// v---block----v
{key: "value"}
// ^--label
For the console to interpret it as object literal syntax, it needs to be part of an expression. If you wrap in parens, you'll get the expected result.
({key:"value"})
Side note:
Here's the really weird part. A statement is a statement because it doesn't return anything. So why does it return "value"?
In JavaScript, statements have something like a final value. (I don't remember exactly what it's called.) It isn't anything that's useful or reachable in program code, but when a program is evaluated, that final value will be returned to whatever evaluated it.
The same goes when you use eval() to evaluate a program. Its final statement value will be returned. Since code in a console is eval'd, it gets that final value and prints it.
That's because what you type is interpreted.
{key:'value'} is a block and returns "value". You'd have had the same result with just key:'value' or 'value'.
If you want an expression returning your object, type ({key:'value'}).

Why is for(;;){…} an infinite loop? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Empty for loop - for(;;)
I just found a strange construct in the JS parser of UglifyJS (at L1045): for(;;){…}.
I assumed that an empty condition would resolve to undefined, which is converted to the boolean value false. But that's definitely not the case.
Apparently, it triggers an infinite loop. I was able to reproduce this behavior, but I have no clue why. Any (logical) explanations?
Besides: When this is possible, why doesn't while(){…} work?
That's just the definition of the semantics. A missing "test" expression is treated as an expression with the value true. Languages are made up by people, and they are at liberty to specify any behavior they like. Clearly, that behavior is something Mr. Eich likes :-)
for(;;){…} interprets an empty condition as true and while(){} is not considered as valid. As said before it's totally language dependant but described in the specification.
In the ECMA-262 language specification of JavaScript (section 12.6.3) it is defined how the behaviour of the for loop should be.
You can see from the definition that if the information around and between the semi-colons is not available, there are no conditions to leave the loop. The only way to leave the loop is by defining a test condition and optionally some start and step values.
The behaviour could be defined in a different way but that's just not how it is.
From the spec.
12.6.3 The for Statement
The production
IterationStatement : for (ExpressionNoIn(opt) ; Expression(opt) ; Expression(opt)) Statement
is evaluated as follows:
1. If ExpressionNoIn is present, then.
a. Let exprRef be the result of evaluating ExpressionNoIn.
b. Call GetValue(exprRef). (This value is not used but the call may have side-effects.)
2. Let V = empty.
3. Repeat
a. If the first Expression is present, then
i. Let testExprRef be the result of evaluating the first Expression.
ii. If ToBoolean(GetValue(testExprRef)) is false, return (normal, V, empty) .
b. Let stmt be the result of evaluating Statement.© Ecma International 2011 91
c. If stmt.value is not empty, let V = stmt.value
d. If stmt.type is break and stmt.target is in the current label set, return (normal, V, empty) .
e. If stmt.type is not continue || stmt.target is not in the current label set, then
i. If stmt is an abrupt completion, return stmt.
f. If the second Expression is present, then
i. Let incExprRef be the result of evaluating the second Expression.
ii. Call GetValue(incExprRef). (This value is not used.
Gist of this spec: for statement stops when first Expression returns "falsey" value.
Since absence of expression doesn't return false, the script will run forever (or until break statement is executed from inside the loop body).

Categories

Resources