JavaScriptの即時関数(IIFE)とは?書き方と先頭の「!」やカッコの意味
即時関数(IIFE)とは
即時関数(IIFE: Immediately Invoked Function Expression)は、定義と同時に実行される関数です。関数式を () で囲み、その直後に () を付けて呼び出します。
(function () {
console.log('定義と同時に実行されます');
})();
// アロー関数でも書けます
(() => {
console.log('こちらも即時実行されます');
})();
即時関数にしても、処理の実行順序は変わりません。次のコードは、関数を使わず直接 console.log を3つ並べた場合と同じ結果になります。
console.log('スクリプトの先頭です。');
(function () {
console.log('スクリプト実行中。');
})();
console.log('スクリプトの末尾です。');
スクリプトの先頭です。
スクリプト実行中。
スクリプトの末尾です。
なぜ必要なのか:変数スコープを閉じ込める
即時関数の目的は、変数のスコープを関数内に制限し、グローバル変数の汚染を防ぐことです。
関数の外で var を使って変数を定義すると、その変数はスクリプト全体(ブラウザではグローバル)から参照できてしまい、他のスクリプトの変数と衝突する恐れがあります。
var foo = 'スクリプトの途中で定義されました';
console.log('スクリプト実行中。', foo);
console.log('スクリプトの末尾です。', foo); // どこからでも参照できる
即時関数の中で定義した変数は、関数の外からは参照できません。
(function () {
var foo = '即時関数の中で定義されました';
console.log('スクリプト実行中。', foo);
})();
console.log('スクリプトの末尾です。', foo);
スクリプト実行中。 即時関数の中で定義されました
Uncaught ReferenceError: foo is not defined
即時関数の省略記法(先頭の「!」の意味)
function 宣言は文の先頭に書くと「関数宣言」として解釈されてしまい、そのままでは () で呼び出せません。そのため、関数を「式」として扱わせることで即時実行を成立させます。カッコ (...) で囲むのが最も一般的で、ほかにも単項演算子を先頭に付ける方法があります。
(function () {
const foo = 'a';
})();
!(function () {
const foo = 'b';
})();
+(function () {
const foo = 'c';
})();
-(function () {
const foo = 'd';
})();
void (function () {
const foo = 'e';
})();
いずれもエラーにならず、関数が即時実行されます。! は文字数が少なくて済むため、圧縮されたスクリプトでよく見かけます。
! や + を付けた場合、即時関数の返り値に演算子が適用されます(! なら真偽値の反転)。返り値を使いたい場合は、カッコで囲む書き方を使ってください。
また、前の行がセミコロンで終わっていないと、先頭の ( が前の式の関数呼び出しとして解釈される事故が起きます。即時関数の前の行には必ずセミコロンを付けましょう(先頭に ; を置く書き方もあります)。
現在でも即時関数は必要?
ES Modules(<script type="module"> や import / export)では、ファイル自体が独立したスコープを持つため、スコープを閉じ込める目的の即時関数は基本的に不要です。また、let / const はブロックスコープなので、{ ... } で囲むだけでも変数を閉じ込められます。
一方で、次のような場面では今でも使われます。
- 通常のスクリプト(モジュールでない
<script>)で、グローバルを汚染したくない - トップレベル
awaitが使えない環境で、async即時関数を使う
(async () => {
const response = await fetch('/api/data');
console.log(await response.json());
})();