比較的新しいプログラミング言語でよく聞く「無名関数・関数リテラル・クロージャ」の微妙な関係について
「無名関数・関数リテラル・クロージャ」について
結論からいうと、知らなくても同じ動作をするプログラムは書ける。
ただし、知っているとキレイに分かりやすく短く書けるよ!って事です。
※サンプルコードは、全てjavascriptです。
比較表
名前 | 概要 | メリット |
---|---|---|
無名関数 | 関数名が不要な関数 | 処理や変数をブロック化(変数のスコープを閉じれる。変数名の重複を気にしなくてもいい) |
関数リテラル | 変数に関数を代入できる | 動的に最適な関数を代入できる |
クロージャ | 関数の中に関数を定義できる | グローバル変数を使わなくても値を保持できる |
まず無名関数とは、その名の通り関数名が無い関数です。
1 2 3 |
function(){ alert('hello'); }(); |
これは、関数無しでも同じ動作をします(無名関数は、関数定義と実行を同時に行っているので)
1 |
alert('hello'); |
じゃあ、何が嬉しいの?というと処理や変数をブロック化(変数のスコープを閉じれる。変数名の重複を気にしなくてもいい)というメリットがあります
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// グローバル変数を宣言&初期化 var i = 0; //無名関数を定義・実行する (function(){ var i=0; i = 10; //グローバル変数と同じ変数名を使って、値を上書きしている! alert(i); // 10 })(); //無名関数その2を定義・実行する (function(){ var i=0; i = 20; //グローバル変数と同じ変数名を使って、値をさらに上書きしている! alert(i); // 20 })(); // グローバル変数は、元の値を保持したまま! alert(i); // 0 |
自分一人で短いコードを書いている分には、あんまり有り難みは感じないですが、複数人で開発したり、他人が書いたライブラリを利用する時に関数名・変数名の重複を気にしなくてもいいのは有り難いです(逆にバンバン再利用するつもりなら普通の関数)
次は関数リテラルの話です。
そもそもリテラルって何よ?って感じですが、プログラミング言語におけるリテラルとは即値(そくち、Immediate)の事です
str = “hello”
i = 0
“hello” とか 0 みたいに、値が直接コードに書かれている奴です。
これは分かりやすい反面、コメントや前提知識がないと、値の意味が不明(マジカルナンバー)になりがちです…。
それじゃあ、関数リテラルって何さ?となるのですが、変数に関数を入れられる仕組みの事です。
1 2 3 4 5 6 7 8 |
//関数を定義 function PlusOne(i){ return i+1; } //変数に関数を代入! var var_func = PlusOne; // 変数なのに、関数っぽい動きをする alert(var_func(10)); // 11 |
を実行すると、11って値になる。ただ、これって何が嬉しいの?最初からPlusOne(10)って書けばいいだけじゃん。って感じると思う。
関数の中に関数が入れられるメリットは、条件に合った関数が自動的に代入され(もちろんコーディングした通りに)、変数から実行する時に最適な関数が入っている
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//関数を入れるための変数を定義 var HelloFunc; //「時」の取得 (0~23) myH = (new Date()).getHours(); //時間帯によって、使用する関数が決定する if ( myH <= 5 ) HelloFunc = function early(){alert("まだ寝ています")}; else if ( myH <= 11 ) HelloFunc = function moning(){alert("おはよう")}; else if ( myH <= 18 ) HelloFunc = function afternoon(){alert("こんにちは")}; else if ( myH <= 23 ) HelloFunc = function night(){alert("こんばんは")}; // あいさつしたいだけの関数。呼び出す側は、内部にどんな関数が入っているかは関知しなくてもいい。 // 同じコーディング(HelloFunc)で、動的に関数を指定出来るって所がミソ! HelloFunc(); |
これだけシンプルだと、あんまり有り難みが感じづらいかもしれませんが、関数の呼び出し側は出来るだけシンプルかつ何も考えずに実行出来る方がいいですよね?(あいさつしたいだけ)
最後のクロージャというのは、関数の中に書くインナー関数の事(基本的に無名関数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function test(){ var i=0; //これがクロージャ(inner関数) return function(){ //久々の前置インクリメント。returnする前に+1 return ++i; } } // test関数の戻り値はinner関数(関数リテラル) var counter = test(); // 呼び出すたびに+1される // 内部の無名関数がコールされ、外側にあるvar i=0は初期化されないので alert(counter()); // 1 alert(counter()); // 2 alert(counter()); // 3 |
これが出来ると何が嬉しいかというと、グローバル変数を使わなくても値を保持できる(関数が呼ばれる度に+1できる)
このクロージャ機能がないと、コードの頭にグローバル変数だらけになる(^_^;)
※C言語の関数ポインタと似ているけど、var i=0 みたいに変数を保持できるオブジェクト(そういった意味では、クロージャはクラスに近い概念)
「関数、関数、うるさいんじゃ、ボケ!」と言いたくなりますが、慣れてくると割と便利な機能です(^_^;)
こうやって比較的新しい言語の機能を見ていくと「このコードって、もうちょいキレイかつ簡単に書けないのかな?」という先人たちの苦労が忍ばれますね…。