DEV Community

dean
dean

Posted on

Semicolon Rules in JavaScript are weird...

I'd like to preface this with that I really do like JavaScript. It has quirks, although most of them are edge cases that you won't encounter. But I just found one that I'm still confused, and I'd rather not look up the JavaScript spec just to figure this out...

My original function:

function cubicBezier(x, u0, u1, u2, u3) {
    return
        u0 * (1 - x) * (1 - x) * (1 - x) +
        u1 * (1 - x) * (1 - x) * x +
        u2 * (1 - x) * x * x +
        u3 * x * x * x;
}
Enter fullscreen mode Exit fullscreen mode

Some of you seasoned JavaScript experts might see this and go "it will always return undefined", because the return is interpreted as return; since nothing else follows. This got me wondering about semicolon rules in JavaScript.

My favorite programming language is Go, which also has optional semicolons. The semicolon rules are extremely simple in Go:

When the input is broken into tokens, a semicolon is automatically inserted into the token stream immediately after a line's final token if that token is

  • an identifier
  • an integer, floating-point, imaginary, rune, or string literal
  • one of the keywords break, continue, fallthrough, or return
  • one of the operators and punctuation ++, --, ), ], or }

By the way, Go's rules can be interpreted a lot easier like this:

A semicolon is placed if the line ends with:

  • a letter or number
  • a closing punctuation mark (aka ], ), }, and closing "/')
  • ++ or --

I thought that JavaScript Semicolon rules were just as simple as Go's when I saw the function return undefined. After all, nothing appears after the return, so a semicolon was placed afterward. It is that simple right?

Well, I looked into it further.

So I made a few functions for adding 1 to an integer to see what JavaScript did.

function addOneNormal(x) {
    return x + 1
}

function addOneWeird(x) {
    return x
        +1
}

function addOneUndefined(x) {
    return
        x + 1
}
Enter fullscreen mode Exit fullscreen mode

We know what addOneNormal and addOneUndefined end up being. addOneNormal adds one to x, and addOneUndefined hits the return and returns undefined. So what does addOneWeird do?

(sidenote: in Go, this is very simple, as return x ends with a letter, so a semicolon is placed. The next line, +1, results in a compile error as +1 is not being assigned to anything)

Well, some people would expect it to be the same as return x + 1;, although some people (like me) see it as return x; +1;, where the +1 is a 1 with a unary plus operator before it.

The result

So what was the result? addOneWeird(5) => 6. It added 1 successfully. That's weird... isn't it? That statement looked at the next line, even though a bare return didn't.

Unfortunately these rules cannot be made more consistent, as backward-compatibility is a requirement in JavaScript.

Anyway, could someone explain why the + operator ended up being interpreted as a binary plus rather than a unary plus in this case? If the addOneUndefined function resulted in undefined, It seems more logical foraddOneWeird to be interpreted as return x; +1;.

Top comments (6)

Collapse
 
dschu profile image
dschu • Edited

Hey Dean,

great article. :)

Since you seem to like clean, readable code, you may want to put your expression in parantheses, like this:

function addOneUndefined(x) {
    return (
        x + 1
    );
}

function cubicBezier(x, u0, u1, u2, u3) {
    return (
        u0 * (1 - x) * (1 - x) * (1 - x) +
        u1 * (1 - x) * (1 - x) * x +
        u2 * (1 - x) * x * x +
        u3 * x * x * x
    );
}
Collapse
 
dean profile image
dean

Oh, I like this! Thank you :)

Collapse
 
jazkh profile image
jazkh • Edited

When javascript parse your code, it assumes that return is going to actually return something and if it find nothing on the same row, it automatically adds semicolon. In your case, it see x after return then it continues to search next line for the code to be ended with semicolon.

Collapse
 
dean profile image
dean

My question is why is it looking at the next line? When it's just return, it doesn't look at the next line. Why is return x different?

Collapse
 
david_saint_ profile image
David Saint

The return statement is part of some javascript statements that must be terminated with semicolons and is therefore affected by automatic semicolon insertion (ASI) along with others like (continue, import, let, const e.t.c)

There are 3 rules for the ASI, but I think the one you're looking for is this

"A semicolon is inserted at the end when the end of the input stream of tokens is detected and the parser is unable to parse the single input stream as a complete program".

View the rest at developer.mozilla.org/en-US/docs/W...

Thread Thread
 
dean profile image
dean

Interesting! So since return is a keyword that must end with a semicolon, a semicolon is placed. But because return x ends with an identifier, it checks the next line to see if it is parsable as a program, and if it is, then it doesn't insert a semicolon. Neat!