Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Fixes FUD around ASI #83

Closed
wants to merge 2 commits into from

4 participants

@robotlolita

ASI was being referred to as something that changes the behaviour of someone's code at will, like a non-deterministic parser, which couldn't be further from the truth. This patch fixes that by explaining how the ASI rules work instead.

More information on ASI can be found in this site:

http://aresemicolonsnecessaryinjavascript.com/

In particular, Inimino's post is terrific on explaining everything about it, while still managing to be pretty neutral on a particular style — as that's the only thing that should drive the use or not of ASI.

@michaelficarra

While a little biased, I believe the way it is currently is still more informative, especially the big "how it works" example that you removed with comments near ASI points. I agree with some of your changes, but IMO most just make it less helpful for people actually trying to understand ASI. I think your "restricted productions" section is a good replacement for the current "leading parenthesis" section, but the rest is fine the way it is. On a side note, this whole section could use some major copy-editing.

@robotlolita

I'm not sure the current text is really more informative. The FUD aside (which is my nitpicking), the text does not really tell you why semicolons are inserted in the source, it just tells you that they are inserted, and moves on to an example showing some edge cases -- which are the cases where the next line begins with a left parenthesis.

However, it fails to tell people that the left square bracket, the forward slash and the arithmetic plus/minus operators will also cause a semicolon to not be inserted. So, someone would think the following'd be perfectly fine:

function foo(bar, baz) {
    var x = bar + baz
    /foo/(x) && alert('foo')
}

But in effect, the forward slash is treated as a division operator for the preceding statement. Thus the parser would read that as:

function foo(bar, baz) {
    var x = bar + baz / foo / (x) && alert('foo')
}

Since foo is a function object, and x is not defined, it would evaluate x to NaN. This is one of those terribly difficult to spot cases where things don't look like an error (which in fact isn't), so you're likely to overlook it when scanning the source.

There are also other edge cases where semicolon insertion might go by unnoticed, by which I have already been bitten a few times, which are destructuring bindings for arrays:

function foo(x, y) {
    prepare_foo()
    [x, y] = get_foo()
}

Which the current text also does not point out, and which might be way too common to use if you're developing only for SpiderMonkey.

My point is, that while the current text presents an enourmous example of semicolon insertion, it covers only one of the cases, and points little on what a developer should watch out for when writing or reading some code. In fact, it brings, imho, more fear and uncertainity than actual pointers.

That's not to say my text solves all those problems, it could frankly use a whole better wording and explanations, but I guess it tells people what they should be watching for when deciding whether or not to place a semicolon at the end of a statement. I'm not sure which parts you didn't found informative, though, but I can try to improve it :3

@michaelficarra

I agree, it's not currently comprehensive, and yours could be written better as well. I'd say, if you add examples with comments marking ASI points (some people just learn better that way) and reword your changes to be a little easier to read, I'd be all for it.

@metavida

I like the changes, @killdream. How about adding those / and [ examples to implicit line continuation section? They were very helpful for me to see here in the comments. You could also add ; // <- inserted or ; // <- implied in your "after" code samples to make the ASI behavior more clear. Other than that, I really appreciate the reduction of FUD and increase in detail you've added!

@metavida

How about adding "These measures help ensure that JavaScript's interpretation of your code both reflects your intention and is more clear to others reading your code."

@robotlolita

Hm, dear gosh, I totally forgot about this pull request. I'll try to work on it this weekend :3

@timruffles
Collaborator

Hi - bit of a ancient thread. I'm helping maintain JS Garden now, so if you fancy having a look at this that'd be appreciated (and not just because I also avoid the semi-colon ;) )!

I agree with @metavida & @michaelficarra on the changes before it gets merged - otherwise looks great.

@timruffles timruffles closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 39 additions and 88 deletions.
  1. +39 −88 doc/en/core/semicolon.md
View
127 doc/en/core/semicolon.md
@@ -3,112 +3,63 @@
Although JavaScript has C style syntax, it does **not** enforce the use of
semicolons in the source code, it is possible to omit them.
-But 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.
+Every statement in JavaScript may end with either a semicolon or a new
+line.
var foo = function() {
- } // parse error, semicolon expected
+
+ }
test()
-Insertion happens, and the parser tries again.
+The parser sees that as if you had put your semicolons just fine.
var foo = function() {
- }; // no error, parser continues
- test()
-
-The automatic insertion of semicolon is considered to be one of **biggest**
-design flaws in the language, as it *can* change the behavior of code.
-
-### How it Works
-
-The code below has no semicolons in it, so it is up to the parser to decide where
-to insert them.
-
- (function(window, undefined) {
- function test(options) {
- log('testing!')
-
- (options.list || []).forEach(function(i) {
-
- })
-
- options.value.test(
- 'long string to pass here',
- 'and another long string to pass'
- )
-
- return
- {
- foo: function() {}
- }
- }
- window.test = test
-
- })(window)
-
- (function(window) {
- window.someLibrary = {}
-
- })(window)
-
-Below is the result of the parser's "guessing" game.
-
- (function(window, undefined) {
- function test(options) {
-
- // Not inserted, lines got merged
- log('testing!')(options.list || []).forEach(function(i) {
-
- }); // <- inserted
-
- options.value.test(
- 'long string to pass here',
- 'and another long string to pass'
- ); // <- inserted
-
- return; // <- inserted, breaks the return statement
- { // treated as a block
-
- // a label and a single expression statement
- foo: function() {}
- }; // <- inserted
- }
- window.test = test; // <- inserted
+
+ };
+ test();
- // The lines got merged again
- })(window)(function(window) {
- window.someLibrary = {}; // <- inserted
+Some tokens cause an implicit line continuation: `[`, `(`, `+`. `-` and `/`;
+that is, the previous statement will span through the following line if any
+of those tokens appear at the start of the next line.
- })(window); //<- inserted
+ a = b + c
+ (d + e).print()
-> **Note:** The JavaScript parser does not "correctly" handle return statements
-> which are followed by a new line, while this is not neccessarily the fault of
-> the automatic semicolon insertion, it can still be an unwanted side-effect.
+Since the line following the assignment expression starts with one of
+the line continuation tokens, JavaScript will accept it as part of the
+preceding statement.
-The parser drastically changed the behavior of the code above, in certain cases
-it does the **wrong thing**.
+ a = b + c(d + e).print();
-### Leading Parenthesis
-In case of a leading parenthesis, the parser will **not** insert a semicolon.
+### Restricted production
- log('testing!')
- (options.list || []).forEach(function(i) {})
+Some statements require a slight special attention, since they might
+end sooner than you'd expect due to semicolon insertion rules. These
+are called **restricted production** statements, being: post-fix
+increment/decrement (`++` and `--`), return, break, continue and throw.
-This code gets transformed into one line.
+In a nutshell, restricted productions can't have a line break between
+the keyword and the rest of the statement. Thus, inserting a line break
+right after the return keyword, will make the return statement end there.
- log('testing!')(options.list || []).forEach(function(i) {})
+ return
+ { foo: 'foo'
+ , bar: 'bar'
+ }
+
+The previous example will be parsed as a return statement, and a statement
+block, and result in a syntax error:
-Chances are **very** high that `log` does **not** return a function; therefore,
-the above will yield a `TypeError` stating that `undefined is not a function`.
+ return;
+ { foo: 'foo'
+ , bar: 'bar' // error, not valid in a statement block
+ };
+
### In Conclusion
-It is highly recommended to **never** omit semicolons, it is also advocated to
+It is recommended to **never** omit semicolons, it is also advocated to
keep braces on the same line with their corresponding statements and to never omit
-them for one single-line `if` / `else` statements. Both of these measures will
-not only improve the consistency of the code, they will also prevent the
-JavaScript parser from changing its behavior.
+them for one single-line `if` / `else` statements.
Something went wrong with that request. Please try again.