以上、JavaScriptの関数がオブジェクトであり、変数にも自由に代入できることが理解できた。では、関数の引数として関数を引き渡すこともできるのではないか ―― そのとおり。
JavaScriptでは、文字列や数値を引数としてセットするのとまったく同じ要領で、ある関数をそのほかの関数の引数としてセットすることが可能だ。そして、このような関数のことを「高階(こうかい)関数」と呼ぶ。
■高階関数の基本的な例
高階関数を利用した具体的な例をリスト12に示す。
以下のコードで定義するarrayReduce関数は、引数に与えられた配列(ary)の内容を、指定されたユーザー定義関数funcの規則に従って順番に処理し、最終的な結果を戻り値として返すものだ。
function arrayReduce(ary, func) {
var result = ary[0];
for (i = 1; i < ary.length; i++) {
result = func(result, ary[i]);
}
return result;
}
function elementSum(x, y) {
return x + y;
}
var myAry = [5, 3, 4, 7];
var result = arrayReduce(myAry, elementSum);
window.alert(result); // 「19」を表示
arrayReduce関数の引数funcとして引き渡している、配列処理のためのユーザー定義関数はelementSum関数だ。引数funcで引き渡される関数は、第1引数と第2引数とで何らかの演算を行い、その結果値を返さなければならない。
ここでは、elementSum関数は与えられた2つの値を加算する処理を行うので、arrayReduce関数はそれ全体として、配列要素すべての合計を算出するはずだ。果たして、与えられた配列myAryの要素合計19(=5+3+4+7)が結果として返されることが確認できるはずだ。
もちろん、ユーザー定義関数は自由に入れ替えることが可能で、それがここで高階関数を使用している目的だ。例えば、以下のコードはarrayReduce関数に引き渡すユーザー定義関数を、elementSum関数からelementMultiply関数に置き換えたものだ。
function arrayReduce(ary, func) {
var result = ary[0];
for (i = 1; i < ary.length; i++) {
result = func(result, ary[i]);
}
return result;
}
function elementMultiply(x, y) {
return x * y;
}
var myAry = [5, 3, 4, 7];
var result = arrayReduce(myAry, elementMultiply);
window.alert(result); // 「420」を表示
elementMultiply関数では積算を行うため、arrayReduce関数全体として今度は配列要素すべてを掛け合わせたものを算出することになる。結果を確認してみると、確かに配列myAryの要素合計420(=5×3×4×7)が返されることが確認できる。
このように高階関数を利用することで、より汎用性の高い(機能の差し替えが容易な)関数を定義できることがお分かりいただけるだろう。
■高階関数と匿名関数
実は、高階関数は先ほど登場した匿名関数と密接な関係を持っている。というのも、高階関数においては、引数として引き渡す関数が「その場限り」でしか利用されないことがままある。
例えば、先ほどのリスト12、13の例でもユーザー定義関数elementSum/elementMultiplyはarrayReduce関数の処理を規定するために用意した関数であり、これをほかの個所で再利用する予定がないならば、あえて関数に名前を付ける必要がないことが直感的に理解できるだろう。
ということで、そのような場合には匿名関数を用いて、以下のように記述するのが好ましい。
function arrayReduce(ary, func) {
var result = ary[0];
for (i = 1; i < ary.length; i++) {
result = func(result, ary[i]);
}
return result;
}
var myAry = [5, 3, 4, 7];
var result = arrayReduce(
myAry,
function(x, y) { return x + y; }
);
window.alert(result); // 「19」を表示
これが、先ほど関数リテラルは「function文よりも柔軟性のある記述が可能」であると記述した意味だ。
なるほど、匿名関数を用いることで、いわゆる「使い捨て」の関数定義を、高階関数を呼び出すコードにそのまま組み込むことができるので、よりシンプルにコードを記述できることがお分かりになるはずだ。また、関連する処理が1カ所で記述できることから、コードの可読性も向上するというメリットもある。
[コラム]コールバック関数と匿名関数
余談ではあるが、ここで本連載の名前ともなっている「Ajax」に関連する話題についても少しだけ触れておこう。Ajaxプログラミングでは、サーバ側のサービスを非同期呼び出しするに当たって、処理結果を受け取ったときの処理を規定するコールバック関数を宣言する局面が、往々にして登場する。
以下は、ASP.NET AJAX(マイクロソフト社が提供するAJAX対応フレームワーク)を使って、クライアントサイド・スクリプトからサーバ側のサービスを呼び出している例だ。
ServiceBridge.GetAddressByPostnum(
$get('postnum').value,
function(result, cx, name) {
$get('address').value = result; }, [A]
function(ext, cx, name) {
$get('address').value = ext.get_message(); } [B]
);
ASP.NET AJAX固有の構文については、本稿のテーマを外れるので、ここでは割愛する(詳細は別稿「.NET TIPS:[ASP.NET AJAX]クライアントサイド・スクリプトからXML Webサービスを非同期呼び出しするには?(クライアントサイド編)」を参照)。ここでは、サーバ側のServiceBridge.GetAddressByPostnumメソッドを呼び出しており、[A]では処理成功時のコードを、[B]では処理失敗時のコードを定義しているとだけ理解しておけばよい。
上のコードは、匿名関数を使わずに、以下のように記述することも可能だ。
ServiceBridge.GetAddressByPostnum(
$get('postnum').value,
OnSuccess,
OnFailure
);
function OnSuccess(result, cx, name) {
$get('address').value = result;
}
function OnFailure(ext, cx, name) {
$get('address').value = ext.get_message();
}
しかし、このようなコールバック関数(リスト16ではOnSuccessおよびOnFailure)は多くの場合、ほかで呼び出すことがない場合がほとんどだ(OnFailure関数の方は複数個所で共通して利用するかもしれないが)。そのような場合には、先の匿名関数を利用した記述の方がよりシンプルに記述できることがお分かりいただけるだろう。
別に、匿名関数や高階関数はAJAXプログラミングに固有の概念ではないが、コールバック関数やイベント・ハンドラを多用するAjaxプログラミングで多用される概念であるのも間違いない。自分でコードを記述するうえでも、他人のコードを読み解くうえでも重要な知識であるので、ここできちんと理解しておくことを強くお勧めする。
Copyright© Digital Advantage Corp. All Rights Reserved.