JavaScriptの関数を関数に入れる不思議

JavaScriptでは、

・関数を代入できる
・関数を関数の引数に渡すことができる

というのがあって個人的にはすごく不思議な感じがする。

function kansu() {
	window.alert("関数aaaを実行しました");
}

var aaa = kansu;
aaa();

functionを使うことで関数に名前を付けずに(無名関数を)、代入することもできる。

var bbb = function () {
	window.alert("関数bbbを実行しました");
}

無名関数はコンストラクタ(オブジェクトを生成するための関数)の中で使われることが多い。

function Wizard(hp, mp) {
    this.hp = hp;
    this.mp = mp;
    this.fire = function () { window.alert("炎で攻撃"); } 
    this.heal = function () { window.alert("味方を回復"); }
}
var suneo = new Wizard(10,5);
suneo.fire();

関数を関数に入れる(コールバック関数)

JavaScriptでは関数に関数を入れることができる。
入れられた関数のことをコールバック関数と呼ぶ。

以下の例では、filter関数にfunctionを入れて、その結果をhigh変数に入れている。

var cards = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
var high = cards.filter(function(c) {return isNaN(c);});
window.alert(high);

cardsで配列を定義。cards.filterメソッドはfilterに与えられた関数によって実装されたテストに合格した(trueになった)全ての配列からなる新しい配列が生成される。

つまりfilterの引数に無名関数が渡されている。その無名関数への引数はcardsの要素が順番に評価される。isNAN関数は引数が数値じゃなければtrueが。数値ならfalseが返る。

ここではtrueである数値じゃない要素A J Q Kで新たに生成された配列が返る。

JavaScriptのアロー関数と使い方

前章、上の3行のうちまんなかの1行は次のように書き換えることが出来る。

var high = cards.filter(function(c) {return isNaN(c);});

var high = cards.filter(c => isNaN(c));

つまりfunction()を省略し、=>を入れて{}を省略し、returnを省略できる。

記号の羅列の様にしか見えない。覚えるのは無理だ。

配列のsort関数とコールバック関数による並べ替え例

配列arrを宣言し、arr.sort()を活用する。

arrの要素を文字列に置き換えてからUTF16コード順でソートされる。
配列arr自身はソート前後で中身が入れ替わる。

sort()関数の引数に関数(コールバック関数ahoaho)を入れてソート順を自分で指定することができる。

arr.sort(ahoaho);

と言う風に関数名を入れればよい。()や引数は無用。

そして、

・ahoaho(a, b)が0未満の場合、a→bの順でソート
・ahoaho(a, b)が0の場合、aとbは変更せず
・ahoaho(a, b)が0より大きい場合、b→aの順でソート

とソート順を指示することができる。

なぜ2つの引数a,bと書くか。これは各種ソートのアルゴリズムを理解していれば理解できる。
ソートは2つの値を比較して入れ替えるのを繰り返すためaとbを比較すればソートが可能なため。
アルファベットのaとbである理由は慣習的なモノ。

コールバック関数の中は原則こういう風に-1/0/1をreturnするように関数を定義する↓。

//昇順ソート
function ahoaho(a, b) {
	if(a < b) {
		return -1;
	}
	if(a > b) {
		return 1;
	}
	return 0;
}

返り値(-1/0/1)に従って配列の要素が並べ替えが行われる。

単純にarrの中を「数字」で比較する場合は、

function ahoaho(a, b) {
  return a - b;
}

こう簡潔に書くことが出来、原則通り動く。数字の昇順でソートされる。(InfinityやNaNが無ければ)

無名関数を使って定義すると便利。

var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {  return a - b; });

アロー関数でも記述できる。

numbers.sort((a, b) =>  a - b);

arrの中を「文字列の名前順」昇順にソートするには

	var items = ['Banana', 'Apple', 'Durian', 'Cherry'];
	
	window.alert("並べ替え前:" + items);
	
	items.sort(function(a, b) {
		if (a < b) return -1;
		if (a > b) return 1;
		return 0;
	});

	window.alert("昇順ソート後:" + items);
	
	
	function ahoaho(a,b) {
		if (a > b) return -1;
		if (a < b) return 1;
		return 0;
	}


	items.sort( ahoaho );
	

	window.alert("降順ソート後:" + items);

とすれば動く。無名関数を使って記述すると楽↓。

var items = ['Banana', 'Apple', 'Cherry'];
items.sort(function(a, b) {
	if (a < b) return -1;
	if (a > b) return 1;
	return 0;
});

名前の降順でソートするには

items.sort(function(a, b) {
	if (a > b) return -1;
	if (a < b) return 1;
	return 0;
});

とやればOKなのは理解できるだろう。