72 comments

[ 8.1 ms ] story [ 157 ms ] thread
I see these warnings a lot, but I work on a team that does not ever use semicolons, and hasn't for years. It is simply not a problem, I don't know what else to say. And I have come to really dislike the syntactic noise the semicolons add.
Do you start files with a leading ; just in case (of concatenation, where the file otherwise would start with one of `([+-/`)?

Do you use ; at end of var declarations? If not, beware; if so, do you use comma-first? Thanks for answers, just curious.

We don't use semicolons at all. We do use comma-first!

About the concatenation, we use Browserify which bundles our code for us. The beginning of a Browserify bundle is a definition for the `require` function.

don't use a leading ;

always var foo = 'bar';

If you do comma first, I will automatically assume you're an idiot.

One, the leading ; is about concatenation, not variable declaration.

Two, stop making stupid assumptions.

I know what the leading ; is for. I was merely answering the points. Turns out my assumption was right yet again.
I use var foo = 'bar'; but at my place of work they insist on no semi-colons and comma first. I want to tell them this is stupid but I need solid facts. anyone care to expand.
If you happen to write

  var foo = 'bar',
      baz = 'bletch'
  (function (global) {
    //module pattern code here ...
  })(this);
then you have trouble. Semicolons are not optional (meaning always optional, you can leave them out at will). Please read my blog post.
I wish `void` would get more love for IIFEs instead of just sitting there in a dark corner of JS, unloved.

    var foo = 'bar'
    var baz = 'bletch'
    
    void function (global) {
      //module pattern code here ...
    })(this)
Almost perfect, but you left an unmatched close parenthesis before the call operator.
This is a good example! It will try to run 'bletch' as a function. awesome.
Who still writes this in 2015?

Now is:

    module.exports = function(){};
I use a semicolon-free style (only protecting leading ([+-/) with var declarations like so:

    var foo = 1
    var bar = 2
Is there an issue with semicolon-free var declarations that I should be aware of?
I prefer to use it like this ->

  var a = 2
      ,b = "tan"
      ,c = "gent"
  ;
Keeps the var block distinctly visible in a function and in case you add a new variable there's only one line diff.
I like putting a semicolon on every third and fifth line, but omitting it on every fifteenth line and on leap years.
I never tire of comparing Lua with Javascript, and this is another one of those cases. A semicolon in Lua is simply a character which is illegal to place anywhere but the end of a statement.

There is no insertion stage, the language parses just fine on a single line, or any whitespace you choose. Semicolons are vanishingly uncommon in Lua code, but if you want to compress a couple statements onto a line, they signal your intention.

Lua is like what Javascript would be if it were: based on Pascal, written by resource-constrained Brazilians, and capable of making backwards-incompatible changes. Though with the de-facto fork between Lua and LuaJIT, the latter has become much more difficult, as it should at some point in the maturing of a language.

Regarding the resources, we have companies with ridiculously large pockets using Lua, which could contribute more.

Think Blizzard. WoW uses Lua heavily. Though I suspect that niche will eventually be taken away from Lua too, which is unfortunate.

If anything, Javascript suffered because it had large corporations invested in it. The guys at PUC-Rio were never beholden to corporate sponsors as far as I know, and thus free to break compatibility.
That may be true.

But without corporate backing, we wouldn't have had so many amazing implementations, which helped with the adoption.

Even if Lua (or Lua-JIT) can still beat the crap out of them sometimes. Back then there wasn't even a contest.

Why does corporate sponsorship have anything to do with it? Any browser that gained non-trivial market share would have felt the same "don't break the web, or you lose market share" pain that the bigs felt.

KHTML only started getting adoption via the Safari fork because Dave Hyatt and others implemented real-world quirks (e.g., residual style), prior to webkit.org.

Gecko had the benefit of `if (document.all) { IE code here } else { Netscape code here }` patterns from the '90s, but we had to add quirks to gain share with Firefox (m/b, then Phoenix) in 2002 and onward: innerHTML, undetected document.all emulation, and of course XHR.

Corporations have nothing to do with it. Any evolving ecosystem with a huge corpus of popular content begets terrific selection pressure against breaking backward compatibility.

I'm not sure Lua is that different than Javascript here. What is a semicolon in Javascript if not a character you can only place at the end of a statement? There are some similar ambiguities in the Lua parser if you insert a newline into the middle of a statement.

The difference is that Lua is quite a bit fussier about statements versus expressions, and won't allow things like "x() or y()" as statements. So the Javascript example, translated to Lua, wouldn't compile with or without a semicolon.

You can also place a ';' in the for loop, eg

    for(;;){
    }
Those are arguably equivalent to statements, but fair point. Not sure it matters in the Lua vs JavaScript comparison.
There are no such ambiguities. Here is the complete Lua grammar. You may confirm for yourself that semicolons are always optional and that newlines have no semantic meaning.

http://www.lua.org/manual/5.1/manual.html#8

    x = fn (y)
Insert a newline:

    x = fn
    (y)
Syntax error. What happened? The newline has no semantic meaning.
Your problem is not what you think! Quite....

print(foo) ("x"):len()

This creates the same error. Deleting the newline gives you a new, different error. A semicolon give the correct behavior. It's the edge case that the Lua grammar can't resolve, and it does the sensible thing, by refusing to compile.

Also note that it is a compile-time error that is described as an ambiguity. It isn't a silent thing that bites you later, as in Javascript, and the difference is huge.

[added] I'm not sure what you mean re "x() or y()" the following compiles fine:

    function t()
    	return true
    end
    
    function f()
    	return false
    end
    
    if t() or f() then
    	print "logic"
    end
and does indeed print "logic".
Complete example:

    function fn(y)
        return y + 1
    end
    x = fn (1)
    print(x)
Works.

    function fn(y)
        return y + 1
    end
    x = fn
    (1)
    print(x)
Doesn't work. The only difference is a newline; clearly newlines do matter. (And I specifically added the space to show that it's not simply whitespace that matters.) This is, of course, documented in the manual.

For the original example, I meant that expressions are not valid statements.

    true or false
On a line by itself does not compile. It's an expression, but not a statement.

This is one of the things I like about Lua, that it prefers explicit over clever, although sometimes the difference between statements and last statements gets annoying. I'd like to do

    function fn()
        return nil
        -- more code, stubbed out for testing
    end
But you can't have code after return. Instead it needs to be wrapped with if true.
Quite so. I didn't consider this aspect in my first post because it's a syntax error, not an ambiguity; unlike Javascript, Lua won't resolve your ambiguity for you.

I share your feeling about Lua being a bit more statement oriented than I'd like. Have you checked out Moonscript?

Not using semicolons has never be a problem for me.

Just like everyone else I put them where they are needed. I don't put them where they aren't needed, because they aren't needed and adding pointless syntax noise is dumb. If I forget to put them in somewhere they are needed, as anyone might do, I add them. No problems.

The problem is that the rules for remembering where they are needed are hard to remember and error-prone.
Sure, but in practical terms I feel like this never comes up. I just never have to deal with it and I write JS all day.
See http://inimino.org/~inimino/blog/javascript_semicolons, hope it helps. The minimal semicolon style has only a few rules and many people find it easier to use and remember. I've used it but my C hacker habits go against it. It's a fine style.

/be

Wow neat,yeah, js devs are going to call anyone out that doesnt use semicolons.

Question : If you ever wanted 1 breaking change in the language, and somehow it would be possible (I know it wouldn't) what feature would you change ? Again just 1 feature.

I would undo the implicit conversion madness around == and !=.
Is that really a big concern today where == is heavily frowned on by just about everyone due to the above and its slowness?
== is not slow when types of operands (in typeof sense) match -- then it's equivalent to ===.

You'll find a lot of == out there, or would if only Google code search were still up.

Hey, I got to answer with the one thing I'd change. There are more than a few, and I had to pick. Losing the equivalence relation I had in the first ten days (apart from NaN != NaN of course) was my pick.

I treat the issue like the issue of remembering the operator precedence rules or just using some extra parenthesis.

Although I feel remembering where semicolons are needed is simple I still use them anyway for personal preference.

Not harder than inserting a semicolon after every single line.
I haven't used semicolons in JavaScript in places they're not necessary for years now - the only rule that's mattered in practice [1] is lines starting with [ or (.

[1] This is a white lie - there is one additional vital ASI rule, but you already know it - don't put a linebreak after a return statement.

> No problems.

Except that there actually was a problem in this case as evidenced by the surrounding discussion. If the code had been written with a semicolon (or an if statement!) from the beginning, there never would have been a problem. You can argue that it wouldn't have been a problem if JSLint had been written "correctly" from the beginning, but every tool has bugs.

Code defensively, and don't get fancy unless you need to. You're not just complying with the language spec, you are communicating your intent to the other developers who will read and maintain your code.

> Code defensively, and don't get fancy unless you need to. You're not just complying with the language spec, you are communicating your intent to the other developers who will read and maintain your code.

Leaving out semicolons actually makes the intent more clear. People run into problems with semicolon insertion when they try to create an unnamed expression, i.e:

    ["joe", "bob", "ann"].forEach(call);
If you use a no-semicolon style, though, you must name all your expressions, which makes your intent more clear:

    var employees = ["joe", "bob", "ann"]
    employees.forEach(call)
When I mentioned intent, I was mostly referring to the use of an "if" statement vs. short circuiting in the referenced example. But I mean it in general. Every code base is different, of course.

> If you use a no-semicolon style, though, you must name all your expressions.

Thanks, I guess I missed that point.

I see the bug as JSLint's problem, not the code in question (though admittedly that was bad code). I agree that if your tooling requires semicolons you should be pragmatic and include them, but the reality is that parsers/minifiers are mostly built with the assumption that users won't always include semicolons, so that's a largely hypothetical situation.

I definitely agree that you should code defensively, but I'm just not seeing why adding semicolons where ASI kicks in is a practical thing to do. I don't agree semicolons make the code more readable, but if there was such a case you should definitely add them. And everyone - not just people who omit non-ASI'ed semicolons - will forget to include them where needed eventually, but that's not even an especially painful bug.

There are in my opinion more dangerous style choices that are ignored, like daisy-chained declarations. Writing

var x = 1,

y = 2,

z = 3

is a pretty lousy idea, as it unnecessarily couples the meaning of several lines of code together, and makes code harder to edit. If you unthinkingly truncate the first line you get

    y = 2,
    z = 3
a pair of global assignments! That could be a really pernicious bug if you have supposedly 'local' variables sharing the name with an existing global object. It's better to be defensive and write

var x = 1 var y = 2 var z = 3

if only for editability.

My point is that the strong feeling on 'Semicolons' vs 'No Semicolons' is largely a product of focusing bias (http://en.wikipedia.org/wiki/Anchoring); to the extent that it is a problem either way it is a minor one, evidenced by the fact that similar stylistic dangers (see above) are either unfairly ignored or rightly not fretted over.

tl;dr I don't see a practically valuable reason to use them where ASI does the work for you, semicolon vs semicolon is barely a practical issue either way.

I am - I think - looking at this from a safe-by-default perspective. Everyone will forget to include a semicolon eventually as you pointed out, but shouldn't your default strategy be the safest one?

Maybe I am missing something. There are obviously cases where missing semicolons do cause problems. Is there a situation in which adding a semicolon where allowed causes problems or increases the chances of introducing a bug in the future?

What's the default, though? If I try adding semicolons and over-do it, probably I'm ok. But I might make an empty statement while loop body -- an iloop. And being human, I will inevitably leave a crucial semicolon out.

I agree with the parent post. You can go wrong with either C-like or "NPM house" style. Linters are important, moreso in JS than in modern C and C++ (back to the future; I remember old C, which was wildly permissive).

Pick your poison, study it well and dose carefully.

Now I know where my bias comes from: I did not consider going wrong with semicolons in C. Perhaps that is because C is my oldest friend. You are of course correct that the C style is not perfect either. Thanks for the reply.
OCaml's approach to semicolons is nice—they are clearly nothing more than sequence markers between side-effecting expressions.

    a ; b    (* a THEN       b *)
    a   b    (* a APPLIED TO b *)
You also can basically just desugar them to let bindings

    a ; b
    ==
    let _ = a in b

    a ; b ; c ; d e
    ==
    let _ = a in 
    let _ = b in
    let _ = c in
    d e
Due to static typing it's vanishingly unlikely that a forgotten semicolon will properly check.

    let this = x ; y (* if x : 'a       and y : 'b then this : 'b *)
    let this = x   y (* if x : 'a -> 'b and y : 'a then this : 'b *)
So forgetting a semicolon only check if `x` is an unapplied function of type `'a -> 'a` which the value you intended to completely ignore.
The desugar'd version should be

  let () = ...
instead of

  let _ = ...
Huh. Certainly true stylistically, although OCaml accepts things like `1 + 1; print_endline "hello";;` with just a warning.
A while back I think there was a github issue where the authors of some popular library refused to use semi-colons and it was causing all sorts of problems for people. I can't remember what library, but it was funny watching hundreds of people comment on the issue to say "Just use the damn semi-colons!"
Bootstrap. The flamewar wasn't over semicolons initially, the minifier used by Bootstrap was wrong but a work around was to add semicolons.
The first link from the article in question?
Haha, yeah I guess should have followed that link :)
This has never really struck me as a good argument for using or not using semi-colons.

1.) The non-use of semi-colons was perfectly valid JavaScript in that case.

2.) The parser portion of the minifier from Douglas Crockford did not account for this particular case.

3.) It became a flamewar because Douglas Crockford called it out as an error in a very ham fisted way, and the developer responded in kind. If a c++ compiler chokes on valid input, then a bug is sent to the compiler maintainer, and the developer rewrites their code until the bug is fixed.

4.) And that's exactly what happened. The minifier parser was fixed, and the Bootstrap code was updated to work around the parser error.

5.) The reason for not using semi-colons was legitimate. This is a library with a large audience, and not everyone has a minifier/compresser available. It was an easy win for something that wasn't needed. That's exactly the type of stuff that I want when I lean on 3rd parties.

So for me this could have gone a completely different way had the two parties been more civil:

  BS: Hey DC, can you fix your minifier, it looks like it's busted on this valid input.
  
  DC: Hrm, looks like your right.  I'll fix it up, why don't you add a semi-colon in there until I can get it fixed
  
  BS: Hey, no problemo. Done.  I look forward to your fix!
So this is really more of a story about being professional to your fellow developers than anything that has to do with semi-colons or not.
Every time someone brings up semicolons I think it's worth posting Isaac Schlueter's blog post on the topic: http://blog.izs.me/post/2353458699/an-open-letter-to-javascr...

You can get by perfectly fine without semicolons. Going further, if you're using a linter like eslint, I think you can even configure it to give you a warning when the missing semicolon could cause problems.

The biggest problem I have is the "leaders" forgetting the beginners. The whole semicolon thing took JS from "end a statement with a ;" to:

THE RULES:

In general, \n ends a statement unless:

1. The statement has an unclosed paren, array literal, or object literal or ends in some other way that is not a valid way to end a statement. (For instance, ending with . or ,.)

2. The line is -- or ++ (in which case it will decrement/increment the next token.)

3. It is a for(), while(), do, if(), or else, and there is no {

4. The next line starts with [, (, +, *, /, -, ,, ., or some other binary operator that can only be found between two tokens in a single expression.

You have a junior dev now that's 2 steps behind because he's worrying about self-imposed "beatifying" edge cases instead of getting his code to run.

Is ASI really worth the amount of time that has been spent discussing it?

It would be nice if it were deleted from the language entirely.

Can't do that. Web sites rely on it.
I wouldn't be upset if those websites failed.
Unfortunately, it wouldn't be the Web sites failing; it'd be the browsers that decided to break backwards compatibility as users flee them on account of their favorite Web sites not working.
Even as a pragma, a la "use strict"?

"use stricter"?

No, because:

1. Modes are bad for implementations (this feeling was strong from V8 folks such as Erik Corry, but the new crew in Munich may not feel the same way), due to code branching burdens in testing and optimizing while keeping the complexity down.

2. Worse, what you propose would fork the language to double the testing burden for web devs (apart from engine implentors, item 1), since older browsers would ignore the useless expression statement.

This already happened with ES5 "use strict", and created real world bugs (e.g., https://bugzilla.mozilla.org/show_bug.cgi?id=631043, there are more like this).

Developers do not test all the combinations. Notice how a tower of M modes implemented via these string-expression-statement prologue directives can make a 2^M-fold testing burden.

Aren't Google now proposing a bunch of new pragmas?

http://www.2ality.com/2015/02/soundscript.html

As noted in item 1 ("new crew in Munich"), but these may both throw early errors, not let code get to runtime with different semantics (as ES5 strict did, due to arguments object and a few other changes), and use real pragma syntax, which will fail in old user agents.

How can real pragma syntax work on the Web? Only by using a compiler such as Traceur or Babel, otherwise old browsers would choke on the `use types;` unquoted directive.

Note how this can still complicate VMs, but it doesn't fork the test load for web developers. And if the type system results in early errors, plus better optimization with unforked runtime semantics, then the only burden on VM implementors is in the parser and type checker.

We shall see how this experiment works, but in no way is it like `"use stricter";`.

> use real pragma syntax, which will fail in old user agents

> We shall see how this experiment works, but in no way is it like `"use stricter";`.

I might be misunderstanding your comment, but stupidcar's link (http://www.2ality.com/2015/02/soundscript.html) mentions `"use stricter;"` and `"use stricter+types;"` as mode flags, although it also contains a big disclaimer ("The final version of SoundScript may look and work completely different").

Has the syntax been changed since February? If so, I'm interested in reading about it (but my Google-fu is failing); got a link?

That talk on which Axel Rauschmayer reported had some info from a presentation given by Andreas Rossberg of the V8 team to the last TC39 meeting. Here's the PDF:

http://www.mpi-sws.org/~rossberg/papers/JSExperimentalDirect...

There are two ideas:

1. A `"use stricter";` (originally "use sanity" :-/) prologue directive that would not change runtime semantics for code that passed all of static checks, (control flow dependent) dynamic exception-throwing tests, and runtime restrictions on object behavior.

2. A "SoundScript" mode, perhaps enabled by a `use types;` opt-in, or expressed some other way (per module?), that enables gradual type checking based on optional annotations inspired by TypeScript, but with TS-incompatible differences.

Mode 1 might fly with TC39, because it does not fork semantics for code that passes all checks. But for real-world code, there will be divergence when run on old vs. new browsers, so I expect push-back in TC39 on this mode being expressed by a useless string expression statement.

Mode 2 cannot be enabled by a fake-string-expression-statement "prologue directive".

Thanks! This looks promising. Gradual typing is something I've been wanting in JavaScript ever since ES4 got my hopes up.
Which programming language first used a semicolon as a statement delimiter? Did they choose the semicolon because it was easy to type on a QWERTY keyboard? Why not end a statement with a period like a sentence?
The real surprise here is that people still use JSMin, written by an opinionated grump who apparently does not understand the concept of using a real parser. Several superior alternatives exist. UglifyJS is better in every respect.