Dangerous implications of Allman style in JavaScript
Asked Answered
A

5

13

I cannot remember where, but recently I passed a comment where the user told that 1TBS is more preferred than Allman in JavaScript and said Allman has dangerous implications in JavaScript.

Was it a valid statement? If so, why?

Atp answered 28/6, 2012 at 14:54 Comment(2)
There are more statements affected by ASI, check the spec. link that @Esailija posted or give a look to this answer, I summarized the ASI rules there... Ah, and just for fun, try to pass the following test: ASI TestLimit
See also Is block style really this important?Net
E
23

return cannot have LineTerminator after it so:

return
{


};

is treated as return; (return undefined) instead of return {}; (return an object)

See the rules for Automatic Semicolon Insertion (ASI) for more.

Erl answered 28/6, 2012 at 14:55 Comment(6)
Maybe saying that it's related to ASI is a plus to the answer?Ticklish
I have grown to really like javascript, but this is still really weirdPhilips
@FlorianMargaine Give me a momentErl
A good quick rant on using semicolons. To paraphrase: Semicolons are required in JavaScript; they’re just not required in YOUR JavaScript.Kyl
Allman style is a lot more than just return statement. If you return with a named variable there will be no problems. You can use full Allman style everywhere else and I'd claim that using syntax return response is more readable than return { foo:bar, zoo: 1} anyway.Cypher
ASI rules are really weird. If you believe that semicolons are optional and don't want to write those, try this test: asi.qfox.nl – you can keep leaving the semicolons off if you and your whole teams gets full points. Otherwise, stop using that misfeature and write the missing semicolons by yourself.Cypher
T
5

It is a valid statement.

Because JavaScript's engines have what's called ASI (Automatic Semicolon Insertion) which inserts a semicolon if necessary at lines returns. The "if necessary" is ambiguous; sometimes it works and sometimes doesn't. See the rules.

So, as said in the other answers:

return
{
};

// Is read by the JavaScript engine, after ASI, as:
return; // returns undefined
{ // so this is not even executed
};

So it's not recommended for return statements.

However, if your guidelines recommend the Allman style for function declarations, it's perfectly fine. I know some that do.

Ticklish answered 28/6, 2012 at 15:0 Comment(0)
K
3
return {
    a: "A",
    b: "B"
};

// vs.

return // Semicolon automatically inserted here! Uh oh!
{
    a: "A",
    b: "B"
}
Kyl answered 28/6, 2012 at 14:58 Comment(0)
T
2

I think it depends on the statement. For example a return statement might be broken if opening brace is on new line. More info here.

Tanner answered 28/6, 2012 at 14:58 Comment(1)
The link seems to be broken. Also, Stackover style recommends not using link only answers and you should explain the answer here, instead.Cypher
C
1

You can use Allman or Allman-8 style just fine as long as you remember one special case with keywords return and throw.

Separating object literals from keywords return or throw by a linefeed doesn't work in JavaScript because of ASI rules which have a special exception for throw and return statements:

  1. When, as the program is parsed from left to right, a token is encountered that is allowed by some production of the grammar, but the production is a restricted production and the token would be the first token for a terminal or nonterminal immediately following the annotation “[no LineTerminator here]” within the restricted production (and therefore such a token is called a restricted token), and the restricted token is separated from the previous token by at least one LineTerminator, then a semicolon is automatically inserted before the restricted token.

and

NOTE The following are the only restricted productions in the grammar:
[...]
ReturnStatement :
return [no LineTerminator here] Expression ;
ThrowStatement :
throw [no LineTerminator here] Expression ;

(There are other restricted productions, too, but those don't support braces so they are not important for Allman style.)

In practice, this doesn't work as you would expect:

return
{
        status: "successful",
        user:
        {
               id: "9abf38a3-c2f5-4159-a1be-0eccbc1b2349",
               label: "John Doe",
        },
};

because it will be interpreted as (note the semicolon after return!)

return;
{
        status: "successful",
        user:
        {
               id: "9abf38a3-c2f5-4159-a1be-0eccbc1b2349",
               label: "John Doe",
        },
};

So, you have to either use syntax

const response =
{
        status: "successful",
        user:
        {
               id: "9abf38a3-c2f5-4159-a1be-0eccbc1b2349",
               label: "John Doe",
        },
};
return response;

or

return {
        status: "successful",
        user:
        {
               id: "9abf38a3-c2f5-4159-a1be-0eccbc1b2349",
               label: "John Doe",
        },
};

instead.

I personally think that naming the returned structures is more readable in any case, so I use that. I also use Allman-8 everywhere which practically means that you use 8 space width tab characters and indent everything with a single tab character instead of 1 or more spaces.

In theory, somebody could also write

throw
{
        status: "error",
        code: 12,
        details: localvar,
};

which would also fail because a semicolon would be inserted immediately after the keyword throw because of the ASI rules above. In real world, everybody seems to write throw new ... or throw localvar.

I don't know the history for these exceptions but I can only assume it's some historical mishap that cannot be fixed anymore because of existing real world code depending on this mishap. I fail to see any construct where this automatic semicolon would actually be beneficial so I think this is just a bug in historical specification. (The code following return or throw cannot reasonably start with an object literal so it wouldn't work anyway with the inserted semicolon.)

Update year 2024:

I'm nowadays using Allman style where I differentiate between code blocks and object literals. I start all object literals on the same line as the containing statement and all code blocks on a next line. This simple rule also handles the special cases of return and throw as a side effect.

And as a bonus, it makes two different meanings of { visually different. I personally prefer code where understanding it doesn't require back-tracking while reading the code. If you put the opening brace of both code blocks and object literals on the next line, both will look identical but the meaning is totally different.

Cypher answered 2/2, 2022 at 10:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.