I spent a fair bit of time on this Javascript issue (you can tell I am a JS noob):
Take some well written Javascript code like this example of the Revealing Module Pattern:
Running it works fine. Then move the "{" to the next line (as a C# developer I set up all my environments to put curly braces on new lines) and run it again.
return
{
someMethod : myMethod,
someOtherMethod : myOtherMethod
};
It now gets quite a few JS errors around "13 Line breaking error 'return'." and "Uncaught SyntaxError: Unexpected token : " in Chrome Debugger.
My question is, how can something syntactically affect the Javascript like this?
I have set it up here in JSFiddle (to get it to work, move the { after "return" back on to the same line)
One of JavaScript’s worst features is Automatic Semicolon Insertion.
return; // a semicolon is implicitly inserted here
And this part is almost valid JavaScript, but not quite, so you get a syntax error:
{
someMethod : myMethod,
someOtherMethod : myOtherMethod
};
If you had tried to do this:
return
[ 1, 2, 3,
4, 5, 6,
7, 8, 9 ];
it would have just returned undefined all the time, and that would have been bad. ASI sucks, but we’re stuck with it now, especially since semicolonless code has become a fad.
What does this do?
return a
+ b
+ c;
This?
return e
/ f /g;
Okay, okay, maybe that one’s a bit contrived, and maybe this isn’t entirely topical. But ASI is bad. I hope everyone gets that.
Because the ECMA standard section 12.9 states you can't have a new line between the return keyword and its expression.
ReturnStatement :
return ;
return [no LineTerminator here] Expression ;
Javascript does something called Automatic semi-colon insertion which I believe is what is affecting your results here. Basically, it sees the return statement with nothing after on the line, and thinks that's the end of the line and returns, ending the function.
Because return expects a value (or variable), when you put the curly brace in the same line you are telling return a hash but if you move the curly brace to the next line then you are telling return nothing so the next line is not expected.
EDIT
The next line is not expected in the context of return.
Javascript has some strange bits to it and one is that the position of the brackets matters - keep the opening one on the same line as the code
http://encosia.com/in-javascript-curly-brace-placement-matters-an-example/
Related
why this kind of bad design is made on js? Is there any special reason to design the automatic semicolon insertion like this?
Here is my code, It is not work in js in chrome:
(function(){console.log("abc");})()
(function(){console.log("123");})();
Here is the error:
Uncaught TypeError: (intermediate value)(...) is not a function
I know the right version of this code is :
(function(){console.log("abc");})();
(function(){console.log("123");})();
I just want to know why js syntax is designed so stupid. History reason?
I also add this question as a warning to everybody try to use the automatic semicolon insertion of javascript, please just add ; everywhere it needs, the automatic semicolon insertion of javascript is rubbish. It does not work as your expect.
The exist answer is too complex to my case, so I ask a new one:
https://stackoverflow.com/a/2846298/1586797
Another looks good but not work case 2:
x=1
(function(){console.log("123");})()
The linked question's answers explain the spec's three rules for ASI, for example this one. tl;dr:
If it doesn't work, try with semicolon.
The program should end with a semicolon.
If a statement says "can't put newline here", punish it with semicolon.
Your code does not satisfy any of the criteria.
The first line could return a function, and if so that function should be allowed to be invoked; so ( that the second line begins with is not illegal
The first line is not the last line of the program
There is no restricted syntax here.
Therefore, no automatic semicolon for you.
Some people have thus claimed that while (f(){})() syntax is good IIFE syntax, it might be good to do !f(){}() instead:
!function(){console.log("abc");}()
!function(){console.log("123");}();
This works as intended because ! just negates the (discarded) result of the function application, and also because ! as purely unary operator is an illegal character to continue the first line (i.e. f(){}()! is not a thing). This triggers rule 1, and ASI can take place.
The counterargument is that it is butt-ugly (i.e. for anyone not already familiar with the practice, it takes a while for them to understand the purpose of ! in this idiom).
Your second example is similar in nature: as far as the JS parser is concerned, 1 is a value (the fact that it is an integer and could not possibly be a function is a bit lost to it). Look at this example that syntactically is completely equivalent to yours:
a=function(f) { console.log("A CALLED!"); return f; }
x=a
(function(){console.log("123");})()
# => A CALLED!
123
Here, a is a function, so it can be invoked with function(){console.log("123");} as an argument; it returns function(){console.log("123");} unchanged after printing to the console; then () invokes that return value, and 123 is printed as well. Everything works. Thus, Rule #1 is not triggered, no semicolon for you.
(function(){console.log("abc");})()
(function(){console.log("123");})();
is equivalent to:
(function(){console.log("abc");})()(function(){console.log("123");})();
And is what is usually referred to as function currying.
For IIFEs (immediately invoked function expressions) you need to end with ;
For more on function currying see this post. Obviously your console log functions do not work as currying functions, but the syntax yourFunction(a)(b)(c) is a cool feature of the language that is used for currying.
Your code can be simplified as:
(function(){})()()();
This code will get same error.
The () expect a expression to call.
The first () call the (function(){}), the second () call the (function(){})()'s result, but the result is not a function, so it's wrong.
Without the semicolon those statements are chained. You call the first function and give the second func as argument to the return value of the first one. This could actually work, if the first function had a function as return value.
When you expand the code it becomes more obvious:
var a = function(){console.log("abc");};
var b = function(){console.log("123");}
(a)()
(b)();
the last two lines become:
(a)()(b)();
this is equivalent to
var x = a();
x(b);
since a does not return anything, it cannot call it as function with b as argument.
I think this will be clearer if you use this:
x=1
(function(){console.log("123");})()
The error states 1 is not a function. Makes it clear that (function...) is treated like the argument of a function call to 1:
x=1(function(){console.log("123");})()
Because ()() is self-invoking function and ();() are two differnt functions and interpreter is interpreting it accordingly.
Here two pieces of code are totally different for an interpreter.
(function(){console.log("abc");})()(function(){console.log("123");})();
and
(function(){console.log("abc");})();(function(){console.log("123");})();
however, this code will work fine
var a=12
var b=10
console.log(a+b)
Long answer sort :
(function(){console.log("abc");})()
is trying to immediately-invoke the preceding expression which is
(function(){console.log("123");})();
Which would be the return value of the preceding IIFE.
IIFEs can act differently in the missing semicolon situation. That is why we see code as follows:
;(function() {
console.log('abc');
})()
Please have a look of details description here : https://gist.github.com/khellang/8518964
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.
I was expecting JavaScript to reject objects with duplicated properties as invalid but it accepts them in some cases.
{"a":4,"a":5} results in an SyntaxError at least in Firefox and Chrome which seems obvious due to the property a being defined twice.
However ({"a":4,"a":5}) evaluates just fine and results in an object {"a":5} in both Firefox and Chrome.
Why is the expression with the parenthesis accepted?
Summing up the responses: The first example is simply not the construction of an object but a block of labeled statements. Duplicated properities in objects are perfectly valid in which case the last definition wins.
Thanks a lot for your answers!
It is perfectly legal in ECMAScript 3 to declare duplicate properties in an object literal; the SyntaxError you get probably comes from the fact that you used an object literal as a statement, which is not possible due to the confusion with block statements ({ doSomething(); }).
If you want this to be reported as an error, you may want to switch to ECMAScript 5's strict mode: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode.
What you state has no problem if you assign it to a variable, if you don't however, you get the error you mention. Which makes all the difference from a syntax point of view.
When you wrap any structure in parens you are causing that syntax to be evaluated as an expression, the result of which is stored as a temporary variable. The error I get when not doing so in Firefox is unexpected label or invalid label, so it seems without assignment, or parens, this object construction is not treated as an object construction - instead it is treated as a block with multiple label statements that are defined illegally:
{
a: function(){
alert('a');
},
b: function(){
alert('b');
}
}
The above should be totally acceptable as an object, however you get a similar error if you evaluate it without assinging it to some form of variable, or evaluating it with parens. Put simply the duplication of the attribute name is not causing the error :)
Basically imagine your first example, but like this:
function (){
"a": 4,
"b": 5
}
That is roughly how these browsers are treating it, which is now obviously illegal javascript syntax... whereas it wasn't so obvious before.
In the first notation (parentheses-less) the javascript syntax is ambiguous. From ecmascript specification:
An ExpressionStatement cannot start with an opening curly brace
because that might make it ambiguous with a Block.
A block basically evaluates all the statements inside, equivalent to evaluating "a":4,"a":5 which is not valid JS and, in fact, returns the same SyntaxError Unexpected token :
Wrapping that code in parentheses (or, rather, a grouping operator) removes that ambiguity since an assignment expression cannot be followed by a block statement:
var test = {"a":"a","a":"b"}; //test.a === "b"
Furthermore this ambiguity can be removed by any operator or expression that cannot be used with a block statement. A practical scenario hardly comes to mind, maybe if you wanted to return an object literal as part of a conditional operator?
//this *could* come out of a JS minifier:
return x ? (foo(),{"a":"b"}) : (bar(), {"b":"a"});
Why should it not be accepted? You're simply overwriting the values. I think it's rather a feature, than an error. And it works fine for me on various browsers: http://jsbin.com/oculon/1/edit
It's like writing
var a;
a = 4;
a = 5;
alert(a);
it's not Error you just overwrite value with another
I'm guessing (though not certain) that this evaluates as an error because of the difference between the way Firefox and Chrome's JS parsers treat statements and expressions. So because it's wrapped in parentheses the second time, it's considered an expression. Since it's looking for less information in an expression, it can ignore erroneous values. You'll see that if you do...
var a = {'a': 5, 'a': 4};
console.log(a);
It works fine! And also notice that here it's in the right hand side of the statement, giving a hint that it's an expression.
This question already has answers here:
Why do results vary based on curly brace placement?
(6 answers)
Closed 6 years ago.
I am quite new to JavaScript and just started some more serious development in JavaScript. I had a lot of fun implementing the Module pattern. One thing that really drove me crazy was the behavior of the 'return' statement. It is a big difference if you write
Test = ( function()
{
var message = "Hello World!";
return
{
// Does not work
printTest: function() { window.alert(message); }
};
}());
or
Test = ( function()
{
var message = "Hello World!";
return {
// Works well
printTest: function() { window.alert(message); }
};
}());
Note the curly brace after the 'return' statement.
Is that a typical stupid rookie error and is well documented somewhere?
Firebug was not able to give a hint. IE9 and Chrome did report some obscure syntax error at a later location in the code: the opening brace after the 'function' statement in "printTest: function()".
Any comments on this? Are there more such pitfalls in JavaScript?
If you put your brackets to the next line, the interpreter assumes that there is a semi-colon.
So your return statement will be interpreted as:
return;
{
printTest: function() { window.alert(message); };
}
If I remember well, i've red about this problem in JavaScript: The Good Parts
A.3. Semicolon Insertion
JavaScript has a mechanism that tries to correct faulty programs by automatically inserting semicolons. Do
not depend on this. It can mask more serious errors.
It sometimes inserts semicolons in places where they are not welcome. Consider the consequences of
semicolon insertion on the return statement. If a return statement returns a value, that value expression
must begin on the same line as the return:
return
{
status: true
};
This appears to return an object containing a status member. Unfortunately, semicolon insertion turns it
into a statement that returns undefined. There is no warning that semicolon insertion caused the
misinterpretation of the program. The problem can be avoided if the { is placed at the end of the previous line
and not at the beginning of the next line:
return {
status: true
};
"JavaScript: The Good Parts by Douglas Crockford. Copyright 2008 Yahoo! Inc.,
978-0-596-51774-8."
Are there more such pitfalls in JavaScript?
This made me smile :) Oh boy, you're in for a treat.
Have a look at this site for example: http://wtfjs.com/
Yes, it's a difference. I think it actaully is a part of documentation, the subject that is to be returned has to be in the same line as the return statement.
It's beacause semicolons are optional, and the expression is interpreted as
return;
{
....
}
JavaScript does automatic semi-colon insertion. If you put the brackets on the next line after the return, the interpreter automatically inserts a semi-colon after the return, ending the statement.
I remember there is a convention/recommendation to put opening brace in the same line, because of the way JavaScript adds a semicolon or something.
//OK
function blah(){
};
//Probably not OK
function blah()
{
};
But I don't find a relevant source to confirm/deny this.
Is this true? Or just a myth?
The issue you are thinking of is for return statements.
return {
value: 'test'
}
Works fine, but the following does not:
return
{
value: 'test'
}
JavaScript adds a semicolon after return turning the above into:
return;
{
value: 'test'
}
This post on Elegant Code gives some explanation of automatic semicolon insertion, but in regard to returning objects, not declaring functions.
Douglas Crockford gives a reason for choosing the K&R style [1]:
"I always use the K&R style, putting the { at the end of a line instead of the front, because it avoids a horrible design blunder in JavaScript's return statement.
The blunder he is referring to is how JavaScript handles the return statement differently in the following two scenarios:
return {
'status': 'ok'
};
... and:
return
{
'status': 'ok'
};
The first one will return an object with a status property, while the latter will return undefined because of semicolon insertion."
[1] Douglas Crockford: JavaScript: The Good Parts: Style (p. 96)
The JavaScript Garden has a chapter about automatic semicolon insertion. It gives good examples when semicolons are added automatically:
JavaScript is not a semicolon-less
language, it in fact needs the
semicolons in order to understand the
source code. Therefore the JavaScript
parser automatically inserts them
whenever it encounters a parse error
due to a missing semicolon.
In your example JavaScript wouldn't encounter an error due to a missing semicolon though.
There is no issue with declaring functions, but you can get into trouble when returning objects:
function foo()
{ // this is OK
return
{ // this is BAD!
name: "bletch"
};
// actually returns undefined!
}
A semi-colon is automatically inserted after the return statement, and that will break your code.
It's a myth. function blah() is always required to be followed by a expression block, so makes no difference which style you use. The first style is simply the most widely used form.