なぜセミコロンを省いても動くのに、たまに壊れるのか

処理系が改行のところにセミコロンを補っています。ただし補わない場合があり、そこだけ意味が変わります。return の後の改行、行頭の括弧。実際に壊して確かめます。

実行して検証済み処理系Node 22.22.3対応ES1難易度●●○○○目安8更新

処理系が、改行のところにセミコロンを補っているからです。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
3
javascript
function makeUser() {
return
{
name: 'あ'
};
}

console.log(makeUser());
undefined

これを自動セミコロン挿入と呼びます。

補うのは、そのままでは読めなくなったときだけです。 だから「読めてしまう」場合は補われず、書いた人の意図と違う形で通ります。

壊れる例1: return の後で改行する

これがいちばん有名で、いちばん痛い失敗です。

javascript
return;
{
name: 'あ';
}
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
SyntaxError: Unexpected token '}'

return の後ろの改行だけは、仕様が「必ずセミコロンを補う」と決めています。

つまり、こう読まれています。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
{ name: 'あ' }

{ } はオブジェクトではなくブロックとして読まれ、誰にも使われずに捨てられます。 エラーも警告も出ません。

鍵が2つ以上あって末尾にカンマが付いていると、ブロックとしても読めないので SyntaxError になります。落ちてくれるほうが、まだ幸運です。

javascript
const value = 1

(function () {
console.log('動かない');
})()
TypeError: 1 is not a function
javascript
const list = [1, 2]

[3, 4].forEach((n) => console.log(n))
TypeError: Cannot read properties of undefined (reading 'forEach')

括弧を開いてから改行してください。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
3 4
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
1 2

壊れる例2: 行頭が ( で始まる

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
SyntaxError: Illegal newline after throw
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
3

前の行の 1 と、次の行の ( が繋がって 1(...) という関数呼び出しになりました。

1 は関数ではないので落ちます。 → TypeError: x is not a function

壊れる例3: 行頭が [ で始まる

javascript
function f() {
return;
{
a: 1;
}
}

console.log(f());
undefined

[1, 2][3, 4] と読まれ、添字での取り出しになりました。 [3, 4] はカンマ演算子で 4 になるので、[1, 2][4]undefined です。 → TypeError: Cannot read properties of undefined (reading 'x')

文言のどこにも「セミコロン」とは出ません。 だから原因に辿り着きにくいのです。

補われない行頭は5つ

次の文字で始まる行は、前の行と繋がります。

行頭 何になるか
( 前の行を関数として呼ぶ
[ 前の行から添字で取り出す
` 前の行をタグ付きテンプレートとして呼ぶ
+ - 足し算・引き算になる
/ 割り算か正規表現になる

セミコロンを書かない流儀では、この5つで始まる行の先頭にだけ セミコロンを置きます。

逆に、改行で切られてしまうもの

return のほかにもあります。これらは改行を許しません。

  • return
  • throw
  • break / continue
  • ++ / --(前の行との間)

a の後ろでいったん切られ、++b として読まれました。 a は増えていません。

throw は、切られると文として成り立たないので落ちます。

構文の誤りは実行前に止まるので、まだ気づけるほうです。 → SyntaxError: Unexpected token

続きの行は繋がる

途中で終われない形なら、勝手に切られることはありません。

1 + だけでは文として終われないので、次の行と繋がります。 長い式を折り返すときは、演算子を行末に置いてください。関数の基本 — 作る・呼ぶ・返す

結局どうすればよいか

どちらでも構いません。プロジェクトの中で揃っていることのほうが大事です。

流儀 気をつけること
書く return の後で改行しない
書かない ( [ ` + - / で始まる行の先頭に ; を置く

どちらの流儀でも、整形器に任せるのがいちばん確実です。 人が毎回判断するところではありません。

このサイトの掲載コードはセミコロンを書く流儀で揃えています。理由は「return の後で改行しない」だけを覚えればよいからで、優劣の主張ではありません。

よくある誤解

「セミコロンを全部書けば安全」

return の後の改行は防げません。

自分で書いても同じです。位置の問題であって、書くか書かないかの問題ではありません。

「壊れたら必ずエラーになる」

例1がまさにそうです。undefined が返るだけで、何も起きません。 気づくのは、ずっと後の行で undefined を触ったときです。

まとめ

  • 省いても動くのは、処理系が改行のところに補っているから
  • 補うのはそのままでは読めないときだけ。読めてしまう形は繋がる
  • return の後の改行だけは必ず切られる。 値が消える
  • 行頭が ( [ ` + - / の行は、前の行と繋がる
  • throw は改行すると SyntaxError
  • 流儀はどちらでもよい。整形器に任せる

この記事の根拠

  1. Automatic Semicolon Insertion — ECMAScript® 2026 Language Specificationtc39.es
  2. The return Statement — ECMAScript® 2026 Language Specificationtc39.es
  3. 字句文法 — MDNdeveloper.mozilla.org

掲載しているコード例は、公開前に読み手が押したときに動くのと同じもので実行して出力を突き合わせています。結果はリポジトリに残しています。

このページの原文(Markdown)