苦労してるからくr、おや?誰か来たみたいだ
はい。クロージャのお話です。
実際に使うかどうかはアレですが、理解してほしい所であります。
クロージャとはなんぞや?
クロージャ (クロージャー、Closure) は、プログラミング言語において引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決する関数のことである。
貴様は何をぬかしておるのか
兎に角作ってみるのが早いそうですね
作る前に必要なコト
- 関数内の関数
- 無名関数
- 関数を返す関数
関数内の関数
これはそのままの通り、関数内に関数がいるって事なのです。
まぁ、関数の中で関数を定義するって事ですかね。
function aaa() { function bbb() { alert('えええええええ'); } }
さて、bbb関数を実行するにはどうすればよいのか。
わかりますか?ヒントとしては、変数の呼び出しの時、使える場合と使えない場合ってあったはずです。
それと一緒ですね。
無名関数
無名関数のお話はしたと思いますが、少しだけ。
無名関数とはその名の通り、名前がない関数のコトです。
document.getElementById('test').onclick = function() { alert('押したな!焼肉おごれ!'); }
定番の形。function()以降が無名関数になるんですね。
関数を返す関数
関数には引数と戻り値があるって話はよくしてきた話であると思います。
いつもなら戻り値はフラグであったり、文字列であったりでした。
それが今回関数になっただけです。それだけの話ですよ!
function aaa() { var bbb = function () { alert('えええええええ'); } return bbb; } var f = aaa(); f();
ではこいつを動かしてみましょう。コピペでいいですよ。
どないなってるんですかね・・・
順番に何をしているのかを解説入れていきます。
- 関数aaaを生成します。
- 関数aaa内で変数bbbに対して無名関数を渡します。
- 無名関数名の中では、alertされるようになっています。
- aaaはその処理を戻り値として戻します。
- aaaが実行されました。戻り値はaaa内のbbbを返すだけなので、変数fに戻り値が代入されます。
- 代入時点で変数fの中に、無名関数が入ることになり、fは変数から関数に化けます。
- で、変数fを実行すると、alertが出ます。
おどりゃ何ぬかしとんじゃ
bbbの所無名関数でやってますが、普通に関数宣言しても動きます。
function aaa() { function bbb() { alert('あああああ'); } return bbb; } var f = aaa(); f();
すべての基本は変数である。
関数だってそうなんですよ。
さて、無名関数を変数にブチこんで戻していました。
もう一つ処理を簡潔にすると、わざわざ変数に入れる必要なんかこれっぽっちもないんですよ。
function aaa() { return function () { alert('えええええええ'); } } var f = aaa(); f();
短くなったー!☆-(ノ゚Д゚)八(゚Д゚ )ノイエーイ☆
このように、return function...と続けることができます。
戻り【値】とはなんだったのか
まぁ、そう呼んでるから仕方がないんですよ。察して!
で、ここで一つ、変数を入れ込んでみようと思います。
function aaa() { return function () { var text = 'ハラショー'; alert(text); } } var f = aaa(); f();
動かしてみてください。
まぁ、当然のように動いてくれますね。へい。
では以下はどうでしょう?
function aaa() { var text = 'なのです'; return function () { alert(text); } } var f = aaa(); f();
外にある変数は参照できる。その名の通りですね。
戻り値に入れているとはいえ、aaaに内包されているわけなので、aaa内で定義された変数は読めて当然。
これが理解できているか否かでクロージャに対する理解度も違ってきます。
ではここで、もう一つ大事な事。
変数の中身は勝手にリセットされない。
こいつについてやってきましょう
window.onload = function() { var flg = 0; document.getElementById('elem01').onclick = function() { flg = 1; }; document.getElementById('elem02').onclick = function() { flg = 0; }; document.getElementById('elem03').onclick = function() { if(flg == 0) { alert('フラグがたってない'); } else if(flg == 1) { alert('フラグがたっている'); } }; }
実際に動かしてみます。
コピってやってみましょう。
フラグを立てる
フラグをへし折る
フラグを確認する
きちんと変数の中身が保持されているのがわかると思います。
これ、この動き当たり前に使いまくりますが。めっちゃ大事ですよ。
変数の中身は、再定義もしくは代入(変数削除含む)されない限り消えることはない。
とどのつまり、勝手に消えないよって事です。
それを踏まえて以下を。これもコピーでいいので動かしてみてください。
function aaa() { var text = 1; return function () { text++; alert(text); } } var f = aaa(); f(); f();
おかわりいただけるだろうか。
数値が増えているのを。
- 関数aaaを生成します。
- 関数aaa内で変数textを定義し、1を代入します。
- text変数をインクリメントし、それをalertする関数を返します。
- aaaが実行されました。text変数が定義され、インクリメント+alertの処理が変数fに代入されました。
- 代入時点で変数fの中に、無名関数が入ることになり、fは変数から関数に化けます。
- で、変数fを実行すると、textをインクリメントしalertします。
変数fは実行する関数だけなので、関数aaaは読み込まれることはありません、つまり変数textは
再定義も何もされないことになります。
ということは、常に変数の中は保持されていることになります。
で、インクリメントされていくので、どんどん数が増えていくのですね。
というわけで、これがクロージャです。
ここまでくると、最初の一文がわかるようになると思います。
クロージャ (クロージャー、Closure) は、プログラミング言語において引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決する関数のことである。
これを例文に当てはめると
function aaa() { // 自身が定義された環境(静的スコープ)において解決する var text = 1; // 引数以外の変数を return function () { // 実行時の環境ではなく↑ text++; alert(text); } } var f = aaa(); f(); f();
これを説明するのが本当に大変です。わかっていてもね。
つまり、常識中の常識なのですね。