@uents blog

Code wins arguments.

JavaScriptのapplyと再帰呼び出し

最近ちまちまと家でJavaScriptを書いています。

applyについてちょっと勉強してみました。元ネタはサイ本より。

JavaScriptでは可変引数が標準でサポートされているので、任意の引数からの和を算出する関数sum()を作る

function sum()
{
	var total = 0;
	for (var i = 0; i < arguments.length; i++){
		total += arguments[i];
	}
	return total;
}

そこで、

alert(sum(1, 2, 3, 4)); // 結果は10

と上手く行くが、引数が配列だと

alert(sum([1, 2, 3, 4])); // 結果は"01,2,3,4"

となる。[1, 2, 3, 4]という配列オブジェクトはsum()には1つの引数として渡されるため、valueOf()ではなくtoString()が実行されて文字列として評価されてしまう。

こういった場合にはapplyをつかうと便利

alert(sum.apply(null, [1, 2, 3, 4])); // 結果は10

ちなみに第1引数には任意のオブジェクトを渡すことができ、関数本体の中からthisキーワードで参照することができる。

ならば、多重配列でも同じように解けるか言うと、

alert(sum.apply(null, [1, 2, [3, 4], 6, [7, 8, [9]], 10])); // 結果は"33,467,8,910"

と、最初の1, 2は計算されたものの次の要素は[3, 4]のため、文字列で評価され、以降は上手くいかない。


という訳で、オブジェクトの型に合わせて数値変換を試みるsum_flex()を作る

function sum_flex()
{
	var total = 0;
	var element;
	for (var i = 0; i < arguments.length; i++){
		element = arguments[i];
		switch (typeof element){
		case "number":
			n = element;
			break;
		case "object":
			if (element instanceof Array){
				n = sum_flex.apply(null, element);
			} else {
				n = element.valueOf();
			}
			break;
		}
		total += n;
	}
	return total;
}

再帰呼び出しを使う事で、

alert(sum_flex([1, 2, [3, 4], 6, [7, 8, [9]], 10])); // 結果は55
alert(sum_flex.apply(null, [1, 2, [3, 4], 6, [7, 8, [9]], 10])); // 結果は55。もちろん上手く行く

と引数が配列だったらapply()を再帰呼び出しすることで、多重配列でも綺麗に解けるようになる。

実際にはあまり使わないかもしれないけど、結構柔軟。