Most of the comments in this semicolons in JS exchange make me sad. The code in question:
clearMenus() !isActive && $parent.toggleClass('open')
relies on Automatic Semicolon Insertion (ASI) and so cannot be minified except by parsing fully (including ASI), observing the significance of the newline after clearMenus()
, and inserting a semicolon when stripping that newline.
Some argue that JSMin has a bug. Doug Crockford does not want to change JSMin, and that’s his choice.
FWIW, I agree with Doug’s canonically grumpy tone if not his substance; more below on the substance.
I also agree with @cramforce and @jedschmidt that the &&
line is an abusage, allowed due to JS’s C heritage by way of Java, but frowned upon by most JS hackers; and that an if
statement would be much better style (and, I take it, help JSMin do right). But this particular criticism is too ad hoc to help resolve the general “Let me have my ASI freedom and still minify, dammit!” debate.
Doug goes on to say:
TC39 is considering the use of ! as an infix operator. This code will break in the future. Fix it now. Learn to use semicolons properly. ! is not intended to be a statement separator. ; is.
The !
-as-infix-operator idea is proposed as syntactic sugar for promises, which may or may not make it into Harmony with that exact syntax, or with any syntactic sugar at all.
Doug’s right that !
is not a statement terminator or “initiator”. And (my point here), neither is newline.
But search for [nlth]
in the proposed promises grammar and you’ll see something surprising about ASI and infix operators: we can add new infix operators in the future, whether new contextual keyword-operators (e.g., is
and isnt
— BTW these are in doubt) or retasked, existing unary-prefix operators, provided that we insist on [no
LineTerminator here]
immediately to the left of any such infix operator.
(In ECMA-262, [no
LineTerminator here]
is used in so-called “restricted productions” to make contextually-significant newlines, e.g., after return
without any expression of the return value on the same line.)
This future-friendliness to new infix operators comes directly from ASI as a newline-sensitive error correction procedure, as the example at top demonstrates. Try other examples using a leading identifier on a well-formed second line and you’ll see the same effect. Removing the newline introduces an early error, which creates homesteading space for new infix operators in a later edition of ECMA-262. Examples:
let flag = x is y; // no n before 'is'! x ! p = v; // Q(x).put(’p’, v)
An aside on coding style: if we add new infix operators used in restricted productions, this gives weight to the JS coding style that puts infix operators in multiline expressions at the end of continued lines, rather than at the beginning of continuation lines.
So while I agree with Doug on those two lines of code from Bootstrap (an excellent JS library, BTW) exhibiting poor style, it is not the case that such code as written could break in the future, even if we were to adopt the !
-as-infix-operator strawman. The first line terminator in that example is indeed significant.
The moral of this story: ASI is (formally speaking) a syntactic error correction procedure. If you start to code as if it were a universal significant-newline rule, you will get into trouble. A classic example from ECMA-262:
a = b + c (d + e).print()
Similar hazards arise with [
, /
, and unary +
and -
. Remember, if there wasn’t an error, ASI does not apply.
This problem may seem minor, but JS file concatenation ups the ante. For this reason some style guides (Dojo, IIRC) advocate starting your reusable JS file with ;
, but people don’t know and it’s easy to forget.
I wish I had made newlines more significant in JS back in those ten days in May, 1995. Then instead of ASI, we would be cursing the need to use infix operators at the ends of continued lines, or perhaps \
or brute-force parentheses, to force continuation onto a successive line. But that ship sailed almost 17 years ago.
The way systematic newline significance could come to JS is via an evolution of paren-free that makes it to Harmony status. I intend to work on this in the strawman, but not for ES6.
Some of the github issue comments are naive or idealistic to the point of being silly. Since when does any programming language not have syntax arguments? All living, practical languages that I know of, even those with indentation-based block structure and similar restrictions, have degrees of freedom of expression that allow abusage as well as good usage. Language designers can try to reduce degrees of freedom, but not eliminate them completely.
My two cents: be careful not to use ASI as if it gave JS significant newlines. And please don’t abuse &&
and ||
where the mighty if
statement serves better.
I’ll also say that if it were up to me, in view of JS’s subtle and long history, I’d fix JSMin. But I would still log a grumpy comment or two first!
/be