PRにてIIFEを使ったコードがあり、「普通の関数を使いましょうか」と指摘したところ。どういうケースで使うべきかの質問がきたのでメモしておきます。
IIFEとは
Immediately Invoked Function Expressionの略称。雑に日本語にすると、即時に呼び出される関数式。
// case: general function
(function () {
statements
})();
// case: arrow function
(() => {
statements
})();
REF: https://developer.mozilla.org/en-US/docs/Glossary/IIFE
ユースケース
グローバル領域を汚したくないケース
例えば、bootstrap的に初回ロードしたいだけどの関数を実行するとき。
(function(){
preparetion();
bootstrap();
// ...
}();
非同期処理を即時実行したいとき
コールバックの中などでこういうケースに遭遇します。例えば、ReactのuseEffectとかですね。
React.useEffect(() => {
(async function(){
const result1 = await asyncFunc1();
const result2 = await asyncFunc2(result1);
...
})();
}, []()
擬似的なブロックの代入をしたいとき
擬似的なブロックの代入はこのような感じで書けます。ただ、後述しますがIIFEよりも専用の関数を作るほうが大抵の場合はベターです。
// 擬似的なブロック代入
const item = (function() {
let item = fetcheItem();
item.isActive = true;
const serialized = serialize(item);
return serialized;
})();
JavaScriptではこんな感じのブロックの結果を代入できなくて代わりに上のようなIIFEで実現できます。
// JS ではブロックの代入ができない
const item = {
const item = fetcheItem();
item.isActive = true;
const serialized = serialize(item);
return serialized;
}; // エラー
このようにリファクタをしたいと思う動機は、条件分岐が多いときケースやmutableにパラメータを変更する場合にスコープ範囲を狭められるので有効です。
具体例)条件分岐
// 条件が多いケースを愚直にlet使ってJS的に書く
let item;
if(isActive) {
item = fetchedByCondition1();
} else if (isLogin) {
item = fetchByCondition2();
} else {
item = {}
}
// IIFEで書き直し
const item = (function() {
if (isActive) return fetchedByCondition1();
if (isLogin) return fetchByCondition2();
return {};
}()
// 普通に関数で書く
const fetchItem = ({isActive, isLogin}) => {
if (isActive) return fetchedByCondition1();
if (isLogin) return fetchByCondition2();
return {};
}
const item = fetchItem({isActive, isLogin});
このケースの場合はたいていIIFEにしないで普通に関数化したほうがいいですね。
具体例)mutable/immutableのスコープ管理
// mutableなスコープを限定して、グローバルではimmutable化する
// JS的に書くとitemがmutableになる
let item = fetchItem();
item.isActive = true;
item.belongsTo = user;
...
// IIFEでmutableなスコープを限定して、immutable化する
const item = (function(){
let item = fetchItem();
item.isActive = true;
item.belongsTo = user;
return item;
})()
...
これでmutableなスコープを限定しつつimmutableなconstを使うような明示的なコードになりますた。ですが、実際はJavaScriptの場合はconstで定義してもオブジェクトのパラメータは変更可能なので実際は厳格なimmutableではないです。
Swiftなどでは、こういった書き方をすれば完全にmutable/immutableを分離できるのですがJSの場合はあくまで「そのように見せられることができる」くらいです。
とはいえ、個人的にはmutable/immutableの範囲を明示化するほうが好きなのでたまにこういう書き方をします。
おわりに
正直、大抵の場合はIIFEを使うメリットが薄いのでかなりのエッジケースのときだけ使ってます。