JavaScriptの関数
関数の性質
JavaScriptの関数は、数値、文字列、配列などのオブジェクトと同じように扱う事ができる。すなわち
- 変数に代入できる
- 他の関数の引数として渡せる
- 他の関数の戻り値できる
- 実行時に動的に生成できる
- 匿名リテラルとして表現可能である
といった性質がある。このような性質を持つオブジェクトはファーストクラス(第一級)オブジェクトと呼ばれる。
関数を変数に代入する。
function hoge() { window.alert("ホゲ"); } var f = hoge; f(); // "ホゲ"と表示される
関数自身がオブジェクトであるため、普通のオブジェクトのようにメソッドを定義したり、プロパティを付加することができる。ただし、このような利用法はあまりされない。
// 関数fugaがプロパティcountを持っている。 function fuga() { window.alert(fuga.count++); } fuga.count = 0;
関数 static 変数もどき
関数内 static 変数的な値を作る。 非オブジェクト指向言語におけるstatic変数は関数やブロック内で宣言された変数で、そのブロックが終了しても値を保持している変数のこと。
function hoge () { if(!arguments.callee._staticValue){ arguments.callee._staticValue = 1; } return arguments.callee._staticValue++; } hoge(); // 1 hoge(); // 2 hoge(); // 3 hoge._staticValue; // 3 (外部からアクセスできてしまう)
この実体は単なる関数オブジェクトのプロパティなので外部からでもアクセスできてしまう。あまり奇麗な書き方ではないがグローバル変数を定義するよりマシ。
まともな利用法としては、例えば複数のボタンに対して一つの関数をイベントリスナとし、最後にそのリスナを実行したボタン(のDOM要素)をリスナ自身に記憶させるとか。
// require jQuery function onclickHoge( event ) { arguments.callee.lastEventTarget = event.target; // 何か処理 } $(".hoge").click(onclickHoge); // onclickHogeイベントリスナを最後に実行した要素を取得 var element = onclickHoge.lastEventTarget;
無名関数
匿名関数は名前を持たない関数である。名前が無いので、変数に格納したり、別の関数の引数にしなければ呼び出すことができない。
// 無名関数を変数に代入して呼び出す例 var hoge = function() { window.alert("ホゲ"); } hoge();
無名関数は、主に別の関数の引数として使用する場合、オブジェクトのメソッドを定義する場合に使用される。
// addEventListerの引数として無名関数を使う window.addEventListener("load", function(ev){ document.title = "window loadイベントハンドラ"; }, false);
言語によって無名関数の定義はラムダ式と呼ばれる事もある。
var obj = new Object(); obj.x = 1 obj.y = 2 // objオブジェクトのメソッドを定義する obj.sum = function() { return this.x + this.y } obj.sum(); // => 3
オブジェクトのプロパティに関数をセットしたものがメソッドとなる。メソッドとなった関数はthisキーワードで自身のコンテキストにアクセスできる。
高階関数
関数を引数にする関数や、関数を戻り値にする関数を高階関数という。
var array1 = [3,5,4,1,2]; array1.sort(function(a, b){ return a - b }); // 降順ソート var array2 = [4,3,2,5,1]; array2.sort(function(a, b){ return b - a }); // 昇順ソート
Javaのように関数をオブジェクトとして利用することができない(難しい)言語ではストラテジーパターンで代用されています。Javaで配列をソートする場合、Comparatorインターフェースの実装クラスを作る必要があり面倒ですが、スクリプト言語は関数やコードブロックを渡すだけで良く、簡潔に記述できます。
クロージャ
複数の引数をとる関数f(x, y, z, …)があったとき、複数の引数を取る関数fを、1引数の関数の組み合わせで表現する事をカリー化という。
f(x, y, z, ....) = g(x)(y)(z) ....
例
function logarithm(x, y) { return Math.log(y) / Math.log(x); }
これは底をxとする対数 logxy の値を返す関数である。これを1引数の関数に変換してみると…
var logarithmBase = function(x) { return function(y) { return logarithm(x, y); } };
これを実行すると
logarithmBase(10)(2); // => 0.30102999566398114 var commonLogarithm = logarithmBase(10); commonLogarithm(3); // => 0.47712125471966244
logarithmBase(10)を実行すると、あたかも logarithm(x, y) の最初の引数 x が10に固定されてしまったかのように見える。logarithmBaseの定義を見ると、無名関数を定義して返している。この無名関数の内部における変数 x の値は commonLogarithm(3) の時点(実行時)に解決されているものではなく、それよりも前のlogarithmBase(10) の実行時、無名関数が定義された静的スコープで値が解決されている。
このように引数ではない変数を定義時の環境で決定する関数をクロージャという。