即時実行関数式(またはIIFE、発音は「イフィー」、IPA /ˈɪf.i/)は、関数スコープを使用してレキシカルスコープを生成するプログラミング言語の慣用表現です。これは、 CommonJSやES モジュールなどのより標準化されたソリューションが導入される以前は、モジュール型プログラミングをサポートする方法としてJavaScript [ 1 ]で人気がありました。[ 2 ]
即時呼び出し関数式を使用すると、ブロック内からの変数巻き上げを回避し、グローバル環境の汚染を防ぎつつ、関数内で定義された変数のプライバシーを維持しながら、メソッドへのパブリックアクセスを許可できます。言い換えれば、関数と変数をラップし、グローバルスコープから除外してローカルスコープを与えます。
即時呼び出し関数式は、さまざまな方法で記述できます。[ 3 ]一般的な慣習としては、関数式(および必要に応じて呼び出し演算子)を括弧で囲み、[ 4 ]パーサーに式を期待するように明示的に指示します。そうでない場合、ほとんどの場合、パーサーはキーワードに遭遇すると、それを関数式ではなく関数宣言(ステートメント)として扱います。[ 5 ] [ 6 ] function
( function () { /* ... */ })(); ( function () { /* ... */ }()); (() => { /* ... */ })(); // ES6 の矢印関数を使用 (ただし、括弧は外側のみ使用可能)式が期待される状況では、括弧で囲む必要はありません。
let f = function () { /* ... */ }(); true && function () { /* ... */ }(); 0 , function () { /* ... */ }();スコープに変数を渡す方法は以下のとおりです。
( function ( a , b ) { /* ... */ })( "hello" , "world" ); ( function ( a = "hello" , b = "world" ) { /* ... */ })(); // これも動作しますJavaScript の自動セミコロン挿入(ASI) が問題を引き起こす可能性があるケースの 1 つは、先頭の括弧です。この場合、式は前の行の最後の項への呼び出しとして解釈されます。オプションのセミコロンを省略するスタイルの中には、セミコロンを括弧の前に置くものがあり、これは防御セミコロンとして知られています。[ 7 ] [ 8 ]例:
a = b + c ;( function () { // コード})();...として解析されるのを避けるためc()。
IIFE などのデザインパターンを理解する鍵は、ES6 以前の JavaScript は関数スコープのみを備えており(ブロックスコープが欠如していた)、クロージャ内で値を参照渡ししていたことを認識することです。[ 9 ]しかし、ES6 バージョンの JavaScript では新しいキーワードを使用してブロックスコープが実装されているため、これはもはや当てはまりません。[ 10 ]letconst
// ES6以前: IIFEを使用してスコープを作成するvar foo = 1 ; var bar = 2 ; ( function (){ var foo = 3 ; //外側の `foo` をシャドウするbar = 4 ; // 外側の `bar` を上書きする} )(); console.log ( foo , bar ); // 1 4// ES6以降: letとconstと組み合わせて波括弧を使用してスコープを作成するconst foo = 1 ; let bar = 2 ; { const foo = 3 ; // 外側の`foo`をシャドウするbar = 4 ; // 外側の`bar`を上書きする} console.log( foo , bar ) ; // 1 4ブロックスコープがないということは、例えばfor ループ内で定義された変数は、その定義が囲んでいる関数の先頭に「巻き上げられる」ことを意味します。外側の関数によって変更された変数(反復処理によるものを含む)に依存する関数を評価するのは困難になる場合があります。関数の定義と呼び出しの間に値を更新すると、ループを使わなくてもこのことがわかります。[ 11 ]
let v , getValue ; v = 1 ; getValue = function () { return v ; }; v = 2 ;getValue (); // 2手動で更新する場合は結果が明白に見えるかもしれませんが、ループ内で定義されているv場合は意図しない結果が生じる可能性があります。getValue()
以降、関数はv引数として渡され、内部関数の実行コンテキストを保持したまま即座に呼び出されます。[ 12 ]
let v , getValue ; v = 1 ; getValue = ( function ( x ) { return function () { return x ; }; })( v ); v = 2 ;getValue (); // 1これは以下のコードと同等です。
let v , getValue ; v = 1 ; function f ( x ) { return function () { return x ; }; }; getValue = f ( v ); v = 2 ;getValue (); // 1IIFE は、アクセス可能な関数のプライベート メソッドを確立しつつ、後で使用するためにいくつかのプロパティを公開するのにも役立ちます。[ 13 ]次の例は、IIFE に関する Alman の投稿からのものです。[ 1 ]
// "counter" は、プロパティを持つオブジェクトを返す関数です。この場合、プロパティは関数です。let counter = ( function () { let i = 0 ;return { get : function () { return i ; }, set : function ( val ) { i = val ; }, increment : function () { return ++ i ; } }; })();//これらの呼び出しは、 「 counter 」によって返される関数プロパティにアクセスします。counter.get (); // 0 counter.set ( 3 ); counter.increment ( ); // 4 counter.increment ( ); // 5グローバル環境から にアクセスしようとするとcounter.i、呼び出された関数内に含まれているため、 のプロパティではなく、未定義になりますcounter。同様に、 にアクセスしようとするとi、グローバル環境で宣言されていないため、エラーが発生しますi。
元々は「自己実行匿名関数」として知られていたが、[ 14 ] Ben Alman は、comp.lang.javascript で議論が起こった直後に、この慣用表現のより意味的に正確な名前として現在の用語 IIFE を導入した。[ 1 ] [ 15 ] [ 16 ]
特に、即時に呼び出される関数は本質的に匿名である必要はなく、ECMAScript 5 の厳格モードでは[ 17 ]arguments.calleeが禁止されているため、元の用語は誤称となっています。