ボクココ

個人開発に関するテックブログ

JavaScript 〜高階関数とクロージャ〜

JavaScriptでの大きな特徴と言えば、やはり「関数も変数(オブジェクト)として代入できる」という点ではないでしょうか。例えば、


var func = function(){ document.write(1);};
func();  #=>1
といった感じ。
これができるからこそ、色々と応用的なことができるのです。
そのトピックとして大きく二つあります。それが高階関数とクロージャ。最初は私も理解するのに時間がかかりましたが、上の基本を常に頭に入れておくことで理解することができました。


高階関数
変数っぽく扱えるなら、関数の引数に関数のオブジェクトを入れられるでしょ!という考えです。そういやC言語でも関数ポインタとか言うのありましたっけ・・。これを使うと何ができるか?自由度の高い関数が定義できます。今回は例としてRubyで言う配列のeachメソッドを実装してみました。

クロージャ
名前の通り、関数の中に関数を閉じ込めてしまおうという感じ。どういうことかというと、今回は戻り値に関数を指定してみようということです。こうすることで、値を保持する事が可能となります。まるでオブジェクト・グローバル変数のように。それが関数だけでできちゃうなんてちょっと驚きですね。


例えこれらがわからなくてもJavaScript&HTMLとして大事だと思うことがもう一つあります。それがJavaScriptとHTMLの分離。これからはJavaScriptとHTML、CSSのコーディングは分別して作業を行っていくことが多いと思われます。そんな中でJavaScript担当者がスタイルシートをいじったり、HTML担当者がJavaScriptをいじったりしてしまうような場面があるとお互い面倒なことになりがちです。ということでこれからはそういうのも意識して行かないとダメっぽいです。
よくある例としては

<input type="button" value="submit!" onclick="myfunc()>
というもの。よくあるJavaScriptの教科書の一番最初に出てきそうですよね。これだとHTMLの中にJavaScriptのコードが入ってるからあんま良くないらしいです!ということで今回のコードでそれがどうなっているかも注目してほしい所です。それでは以下作ったソースコード
HTML

<?xml version="1.0" encoding="windows-31j" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j" />
<script type="text/javascript" src="closure.js"></script>
<title>クロージャ・高階関数</title>
</head>
<body>
<input type="button" id="high" value="高階関数の実行"></input>
<div id="show"></div>
<br/><br/>
<input type="button" id="closure" value="クロージャの実行"></input>
<div id="show2"></div>
</body>
</html>

javascript


//ロードした瞬間にこの関数を呼び出す
window.onload=function(){
myhighfunc();
myclosure();
}
//高階関数の例。今回はArray#eachメソッドを実装
function myhighfunc(){
document.getElementById("high").onclick=function(){
var arr = [4,7,2,1];
var sum = 0;
//第二引数が関数! そのまま入れちゃってます。
each(arr,function(key,value){
if(key==arr.length-1)
document.getElementById("show").innerHTML += value+"=<br/>";
else
document.getElementById("show").innerHTML += value+"+";
sum+=value;
}
);
document.getElementById("show").innerHTML += sum+"<br/>";
}
}
//eachメソッドの定義。第二引数の関数をデータ毎に実行しろという命令
function each(data,f){
for (var i in data) {
f(i,data[i]);
}
}
//ここからクロージャ。今回はcountの値を保持しているのを見てみる
function myclosure(){
var reclo=closure(0);
document.getElementById("closure").onclick=function(){
document.getElementById("show2").innerHTML += reclo() + "<br/>";

}
}
//関数を戻り値としている。このcountはclosure関数の中。
//つまりこの匿名関数はcountの値を保持し続けている!
function closure(init){
var count=init;
return function(){
return ++count;
}
}
ちょっとだけコメント書いておいたけど、それでもわからん所があれば頑張れ! or コメントにでも書いておいてください。