概要
一度は耳にしたであろう「即時関数」「クロージャ」について簡易的にまとめました。
即時関数
名前の通り、即実行される関数のことですね😋
通常、無名関数を実行する時は、下記のように一旦変数に格納して括弧演算子で実行したりします。
var hello = function(name) { return `${name}さん、Hello!!`; };
hello('太郎') // 太郎さん、Hello!!
ですが、JavaScriptには即時関数と呼ばれる構文が存在します。
上の例を即時関数で書き換えると、以下のような記述になります。
(function(name) { return `${name}さん、Hello!!`;})('太郎'); // 太郎さん、Hello!!
上のように括弧(グループ化演算子)で囲むことにより、定義と実行が行われるのですね。
- ちなみに、以下のように即時関数に関数名をつけたりもできます。
(function hello(name) { return `${name}さん、Hello!!`;})('太郎'); // 太郎さん、Hello!!
さて、即時関数はどういった時に使用するのでしょうか?
これは、グローバルスコープの汚染するを防ぐ方法として利用されたりしています。
ちなみに、ここでの「グローバルスコープの汚染する」は、グローバルに変数や関数を宣言することを指します。
複数人での開発したり複数のライブラリ読みこんだりしている場合に、グローバルスコープが汚れてしまうことで、
変数名や関数名が重複してしまうといったことが起きたりしてしまいます🤮
このグローバルスコープの汚染するを防ぐ方法ですが、即時関数だけではなく他にも方法があります。
オブジェクトを名前空間として用いる方法です。
var myHello = myHello || function(name) { return `${name}さん、Hello!!`; };
myHello('太郎') // 太郎さん、Hello!!
上のように、myHello
が既に定義されているときはそっちを優先みたいに名前の衝突は防げますね😏
他にも、関数を名前空間として用いる方法もあります。
関数内で宣言される変数は、関数内にスコープを持ちます。(ローカル変数と呼びます。)
これは、関数の中からはアクセスできるけど外からはアクセスできません。
この性質を利用してしまえば名前の衝突をグローバルは汚れません。
var user = 'グローバル太郎';
(function(){
var user = 'ローカル太郎';
var user2 = 'ローカル太郎2';
})();
console.log(user); // グローバル太郎
console.log(user2); // ReferenceError
見事に、「グローバル太郎」を「ローカル太郎」にしなくて済みました。
ここで紹介した関数内にスコープを持つという性質は結構重要な項目でして、次項で説明するクロージャでも利用されます。
クロージャ
クロージャとはMDNによると、下記のとおりです。
クロージャは、関数と、その関数が宣言されたレキシカル環境の組み合わせです。
ソースコードで表すと、以下のような感じです。
function makeCounter() {
var count = 0;
function f() {
return count++;
}
return f;
}
var counter = makeCounter();
上のcounter
がクロージャにあたります。
意味合い的には、makeCounter関数内のf関数がクロージャとなってcounter変数に格納されたという感じですかね。
ちなみに、クロージャの仕組みについては、ScopeChainとかの説明が必要で長いので詳細は割愛します😔
(このサイトとか参考になります!)
さて、このクロージャとやらの何がすごいのか。
実は、変数の永続化や隠蔽を実現することができます。
前項でも触れましたが、ローカル変数 (関数の中で宣言された変数) というのは、関数の外からアクセスすることができません。
また、本来、ローカル変数はその関数が実行されている間だけ存在します。
function makeCounter() {
var count = 0;
console.log('makeCounterのローカル変数', count)
function f() {
return count++;
}
return f;
}
var counter = makeCounter(); // makeCounterのローカル変数 0
console.log(counter()); // 0
console.log(counter()); // 1
上のコードを実行してみると、var counter = makeCounter();
のタイミングでローカル変数が存在しているのを確認できますが、counter実行時にもローカル変数countの参照が残り続けていることも確認できます。
ローカル変数countがクロージャの内部状態としてメモリ上に残り続けているのですね。
また、counter()
と実行することで変数の状態を変更できていますね。
まるで、makeCounterクラスのメンバ変数countがクラス内のメソッドfによってのみ変更できる感覚を思い出します🙄
クロージャはカプセル化されたオブジェクト的な存在というわけですね😎
以上、「即時関数」「クロージャ」について簡単なまとめでした。