勘違いしていたこと
JavaScript で関数を即時呼び出しするときに、以下の (A) だと SyntaxError になるので (B) のように括弧で囲みます。
// (A) SyntaxError: Function statements require a function name
function() {
...
}()
// (B) OK
(function() {
...
})()
この「構文エラーになるので括弧で囲む」の理由を勘違いしていました。
関数文を関数式にするために括弧で囲む
結合順を制御するために括弧で囲むのだと思い込んでいたのですが、正しくは、関数文(function declaration / function statement)ではなく関数式(function expression)だと解釈させるためでした。
誤った理解
function(){...}() は function() と {...}() だと解釈されるため SyntaxError になる。結合順を制御するために (function(){...})() と括弧で囲むことで function(){...} と () だと解釈されるようになり、SyntaxError にならない。
正しい理解
function(){...}() は関数文だと解釈される。関数名が書かれていないので SyntaxError になる。最後の () はあっても無くても、同じ理由で SyntaxError になる。
グループ化演算子で関数文を関数式にする
「括弧で囲む」の「括弧」とは、グループ化演算子(grouping expression)のことです。
グループ化演算子は (1 + 2) * 3
のように式の中の評価順を制御します。function
は文脈によって文と式のどちらかに解釈されますが、グループ化演算子の中に置くことで式だと解釈されます。
以下のコードはどちらも同じ結果となります。
(function(){...})()
(function(){...}())
minify すると !function(){...}() になる理由
(function(){...})()
を minify すると !function(){...}()
になることがあります1。
// minify 前
(function(){...})()
// minify 後
!function(){...}()
単項演算子(論理否定演算子)である !
を用いて function(){...}()
を関数式だと解釈させています。また、グループ化演算子を用いる(括弧で囲む)よりも 1 文字少ないです。minify するなら単項演算子が良いということですね。
関数文を関数式と解釈させる方法いろいろ
ここまでの内容で、関数文を関数式だと解釈させることができれば良いことが分かりました。
結局のところ、式の中に function(){...}
を入れるだけなので、いくつもの実現方法を考えることができます。
// グループ化演算子
(function(){...})()
(function(){...}())
// 配列リテラル
[function(){...}()]
// 単項演算子
!function(){...}()
+function(){...}()
-function(){...}()
~function(){...}()
// void, delete, typeof も単項演算子
void function(){...}()
delete function(){...}()
typeof function(){...}()
// 変数代入
x=function(){...}()
// カンマ演算子
1,function(){...}()
// テンプレートリテラル
`${function(){...}()}`
アロー関数式の場合
アロー関数式(arrow function expression)の場合も、即時呼び出しするには括弧で囲む必要があります。
// SyntaxError: Unexpected token '('
() => {...}()
// OK
(() => {...})()
ただし、アロー関数式は名前の通り式です。function
のように文脈によって文になったり式になることは無いため、即時呼び出しする方法にバリエーションはありません。
-
「なることがあります」と断定的な言い方をしない理由は、minify ツールに依存した結果であり、他の選択肢もあるためです。 ↩