即時実行関数(Immediately Invoked Function Expression/IIFEs)の概念と使用理由
「即時実行関数(Immediately Invoked Function Expression/IIFEs)」とは、
関数を定義すると同時に即座に実行される関数式を意味します。
この手法は、変数のホイスティングやスコープの汚染を防ぐために使用され、関数ブロック内でコードを記述する際に有用です。関数は定義された直後に即実行され、主にブラウザーのコンパイラによって関数が読み込まれた直後に呼び出されます。
JavaScriptでは、変数のスコープ(有効範囲)が非常にややこしい問題になることがよくあります。
グローバル変数が多くなると、開発者同士や複数のJavaScriptファイル間で衝突が発生したり、変数が意図せず上書きされるなどのスコープの汚染が起こる可能性があります。さらに、var
キーワードで宣言された変数や関数宣言は、スコープの先頭に巻き上げられる「ホイスティング(hoisting)」という動作が発生します。
そのため、JavaScriptの開発者たちは、面倒な変数スコープの問題を解決するために、関数の特性を活用したコーディング手法を取るようになりました。(もちろん、モダンなJavaScriptであれば、moduleを利用することでこうした煩わしい問題も容易に解決できます。)
JavaScriptの関数には大きな特徴があります。
- 関数ブロック(または「関数ボディ」とも呼ばれます)内で宣言された変数は、その関数ブロック内でのみ有効なスコープを持ちます。
- 関数は任意の場所から呼び出して実行することができます。
JavaScriptの開発者たちは、JavaScriptで使われる関数の特徴について考えるようになりました。
関数を定義し、その関数(ブロックまたはボディ)内に変数を宣言して、定義した関数を即座に実行することで、関数内で宣言された変数はその関数内のみ有効なスコープを持つため、この厄介なスコープ問題を解決できるという着想に基づいています。このコーディング手法が「即時実行関数(IIFE)」という表現式です。
以下は即時実行関数の例です。
(function() { // 即時実行関数
let a = 1;
let b = 2;
let c = a + b;
console.log(c);
})();
let a = 2; // 即時実行関数内の変数aと衝突しません
まだ即時実行関数の書き方がわからなくても問題ありません。これで面倒なスコープの問題を解決できました。
即時実行関数をもう一度定義してみます。
即時実行関数とは、
変数のホイスティングを防ぎ、スコープの汚染を防ぐために関数の特性を活用し、関数ブロック内でコーディングする手法であり、定義されると同時に即座に実行される(ブラウザのコンパイラが関数を読み込んだ後に直接呼び出される)関数式を指します。
それでは、次のセクションでは即時実行関数の書き方について見ていきましょう。
即時実行関数表現式の理解
先ほど述べたように、即時実行関数表現式とは、定義されると同時に即座に実行される関数表現式のことです。正確には、ブラウザのJavaScriptコンパイラが関数を読み込んだ後に直接呼び出される関数表現式を指します。
JavaScriptにおける関数の呼び出しは以下のようになります。
関数名();
関数の呼び出しは、関数名
の右側に()
を付けて行います。もちろん、引数がある場合は(引数)
のように呼び出します。
関数宣言と関数式の違いを理解することは重要です。
即時実行関数表現式(IIFE)は関数宣言とは異なり、関数式であり、即座に実行される点が異なります。
この違いは、JavaScriptエンジンがコードを解釈・実行する方法に影響を与えます。
即時実行関数表現式とは
- 関数を定義し
()
や引数がある場合は(引数)
を使って
定義した関数を即座に呼び出す表現式です。
それでは、上記の手順に沿って即時実行表現式を作成してみましょう。
関数を定義し
function fn() { /* code */ }
上記のコード例は、名前付き関数宣言式で関数を定義したものです。
()
や引数がある場合は、(引数)
を使って定義した関数を即時に呼び出します
()
を単に関数名の右側に付けるだけではエラーが発生します。
function fn() { /* code */ }(); // Uncaught SyntaxError: Unexpected token ')'
上記の例はJavaScriptの関数宣言文の形式に合っていないため、エラーが発生します。
以下のように記述すれば、エラーが発生せずに即時実行関数表現式を作成できます
(function fn() { /* code */ }());
関数を宣言し、()
で呼び出した部分を丸括弧で囲むと、エラーは発生しません。
ここで、関数を宣言し()
で呼び出した部分を囲っている丸括弧は、関数を呼び出すためのものではなく、グループ演算子の役割を果たしています。
()
は関数を呼び出すためだけでなく、グループ演算子としても使われます。グループ演算子とは、()
で被演算子を囲み、式の評価順序を優先させるための演算子のことを指します。
関数を宣言し、()
で呼び出した部分をグループ演算子である丸括弧で囲むと、その関数と関数呼び出しが「式(expression)」になります。式とは値(Value)を生成したり、特定の動作を実行するコードの断片を指します。式になるということは、丸括弧で囲まれたコードが実行されることを意味します。
JavaScriptで関数を即時実行する方法はさまざまありますが、大きく二つの種類に分けられます。
- グループ演算子
()
を利用する方法 - 単項演算子の特徴を利用する方法
グループ演算子()
を使う方法
(function fn() {
/* code */
}());
(function fn() {
/* code */
})();
単項演算子の特性を利用する方法
!function fn() {
/* code */
}();
+function fn() {
/* code */
}();
-function fn() {
/* code */
}();
!
、+
、-
などは代表的な単項演算子です。これらの単項演算子の中で、被演算子が右側にある場合は、先ほどのコード例のように式(expression)となり、コードが実行されることを意味します。つまり、即時実行される関数となります。
即時実行関数の使い方
主に匿名関数として使用されます
即時実行関数は関数の定義と同時に即座に呼び出されるため、関数名が不要な場合が多いです。そのため、主に匿名関数が使われます。
(function () {
/* code */
}());
匿名関数を使うことで、他の関数との名前の衝突を防ぐことができ、特に関数が一度だけ使用される場合にはコードの可読性が向上します。
一度だけ呼び出され、再度呼び出すことはできません。
即時実行関数は、関数の定義と同時に即座に呼び出される式であるため、再度呼び出すことはできません。
const fn = (function() {
console.log("一度だけ実行されます。");
}());
// 出力: "一度だけ実行されます。"
fn(); // Uncaught TypeError: fn is not a function
引数を利用する方法
(function(initialValue) {
let count = initialValue;
console.log(count);
}(10));
// 出力: 10
即時実行関数は、引数を受け取って内部で利用できるだけでなく、値を返し、その結果を変数に保存することもできます。
このようにすることで、特定の計算や初期化を即座に実行し、その結果を後で再利用することができます。
const sum = (function(x, y) {
return x + y;
}(5, 7));
console.log(sum); // 出力: 12
上の例では、2つの引数を渡して加算した値を返し、その結果をsum
変数に保存しています。
このように、即時実行関数は実行と同時に値を計算し、その結果を変数に代入する用途にも活用できます。