JavaScriptでは初歩的なコードでは現れないけれどふとした拍子に出てくる単語っていくつかいるような気がします。 特にカリー化、高階関数、クロージャ、ファンクターあたり。関数型プログラミングを進めていたら最初の方に出てきて戸惑ったのが クロージャ ってやつです。走り書きでn番煎じですが調べて噛み砕いた結果の備忘録です。
結論
要は 関数 です。("スコープ" と書いてあることが多いけど、実際に指しているのは関数の模様)
関数の中でも、以下の特徴を持つ関数のようですね。
- 未実行の関数を返す関数である。
- 子の関数からアクセスできる変数を保持する。
ただの関数とは何が違うわけ?
上の(1)だけ見てもJavaで言うところのファクトリメソッド, JavaScriptだと関数ファクトリ(?)っぽいです。 半分合っていそうですが、違いはソースコードを元に見た方が早い。
let outerVar = "outerVar"; function makeInner(params) { const innerVar = "InnerVar"; function inner() { console.log(`I can see: ${outerVar}, ${innerVar}, ${params}`); } return inner; // 未実行の関数を返す } const inner = makeInner("params"); inner(); // --> I can see: outerVar, innerVar, params
上の例だと、makeInner
関数がクロージャになります。inner
関数を返していますね。この makeInner
実行時には inner()
としていないから未実行です。(定義1)
また、クロージャ内のローカル変数は inner
宣言時には関数のスコープ外になっていますが消えません。(定義2)
こうやって親の関数から返却される子の関数から、子&親の関数内/関数の引数/グローバルスコープの変数にアクセスできるところが他の関数とは違うところ。 ただ、変数は残ってはいるもののあくまで 参照のみが残っているだけ であって値がそのまま保持されるわけではなさそう。
(let outerVar ~ makeInnerクロージャの宣言まで同じ) // クロージャから内部の関数を引数を渡して生成する前でも後でも参照する値は変更可能 // outerVar = "Outer2"; // inner() --> outerVar=Outer2 const inner = makeInner("params"); // outerVar = "Outer3"; // inner() --> outerVar=Outer3 inner(); // --> I can see: outerVar, innerVar, params
2番目の例の通り、途中で書き変わると最終的にクロージャから返却される関数の実行結果は変わるようですね。
参考
JavaScript関数型プログラミング 複雑性を抑える発想と実践法を学ぶ (impress top gear)
- 作者: Luis Atencio,株式会社イディオマコムニカ加藤大雄
- 出版社/メーカー: インプレス
- 発売日: 2017/06/09
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る