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;
}
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
}
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)
Hey Dean,
great article. :)
Since you seem to like clean, readable code, you may want to put your expression in parantheses, like this:
Oh, I like this! Thank you :)
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.
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 isreturn x
different?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...
Interesting! So since
return
is a keyword that must end with a semicolon, a semicolon is placed. But becausereturn 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!