クロージャはそこそこ名前は聞くけど、スッキリと理解できていない。「関数は全部クロージャだよ」的な説明を聞いても、なんだかふわふわしているというか。あと、どういう時に使ったら嬉しいのかもいまいちわかっていなかった。簡単にでもこいつが現れる状況と使いどころをまとめてみる。
クロージャって何
簡潔に言うと…
外側のスコープにある変数への参照を保持できる関数の性質
これだけだとよくわからないので、具体的にどういう性質なのか例を見ていく。
関数とクロージャ
普通は関数って実行したらその場限りでしか状態を保持できない。だから当たり前ではあるけど、下記のように同じ関数を実行しようが関数毎に状態は独立していて、共有されるなんてことはない。
const counter = function() {
let cnt = 0;
cnt = cnt + 1;
return cnt;
}
console.log(counter());//1
console.log(counter());//1
console.log(counter());//1
しかし、クロージャという性質を利用すると関数に状態を保持できる。下記のようにカウント回数を実行時に保持できる。グローバル変数とかで共有させる必要がなくなる便利なやつ。
const createCounter = function() {
let cnt = 0;
return function(){
cnt = cnt + 1;
return cnt;
};
}
const counter = createCounter();
console.log(counter());//1
console.log(counter());//2
console.log(counter());//3
これだけだと、グローバルに持たせなくていいな、って感じは伝わるが、別に状態は関数内で持たせようと思えば持たせられる。なのでいつ使うと嬉しいのかわかりづらい。
function counter() {
counter.count = counter.count + 1;
return counter.count;
}
counter.count = 0;
// 呼び出すごとにcountは更新できる
console.log(counter()); // 1
console.log(counter()); // 2
//どこからでも値を変えられてしまうけれども・・・
counter.count = 4;
console.log(counter.count);
クロージャの使いどころ
プライベート変数やメソッドを実現するのに有効。あとは高階関数としての利用などもある。
const example = function() {
//プライベート変数
let privateVar = "hoge";
//プライベートメソッド
function privateFunc() {
console.log(privateVar);
}
//パブリックメソッド
function getValueFunc() {
privateFunc();
}
function setValueFunc(setVal) {
privateVar = setVal;
}
//外部からアクセス可能なものをオブジェクトなどにして返す
return {
getVal:getValueFunc,
setVal:setValueFunc
};
}
const accessor = example();
accessor.getVal();//hoge
accessor.setVal("fuga");
accessor.getVal();//fuga
console.log(example.privateVar);//undefined
example.privateFunc();// TypeError
上記の例ではaccessor変数によって外側からexample内部の変数への参照を保持し続けている。
変数が参照されている以上メモリから解放されないので、データとして保持される。
イメージとしては、
accessor -> getValueFunc -> privateVar
のように見続けている感じ。
まとめ
グローバルに色々持たせると不都合が多いが、状態をなるべく安全に保持できるといいなって場面は結構ある。クロージャを利用するとそういったニーズをさくっと満たせるなと思ったので、ライブラリ等を用意しなくても使えると便利だなと思う。パフォーマンス面への影響とかを考慮できてないので、その辺りも意識して使っていけたら。