高階関数について簡単に言語化してみました。
高階関数とは
高階関数とは、一言で説明すると「関数を受け取ったり関数を返す関数」のことです。
噛み砕くと、
・引数として関数を受け取る
・戻り値に関数を指定する
関数になります。
ちょっとイメージしづらい、、、。
簡単ですが高階関数のコードを書いてみます。
//高階関数を定義します。
//桃鉄ののぞみカードをイメージ笑
function nozomiCard(f) {
let total = 0;
for(let i = 0; i < 5; i++) {
total += f();
}
return total;
}
//高階関数の引数となる関数を定義します。
//1~6の数字を返却
function dice() {
const resultNum = Math.floor(Math.random() * 6) + 1;
return resultNum;
}
nozomiCard(dice);
まず、高階関数のnozomiCardを定義しています。
この関数の引数f
は関数を受け取ります。(今回はdiceを渡しています。)
受け取った関数f
実行し、実行結果を変数totalの中に格納しています。
これをループで5回行い合計の値を戻り値として返却しています。
この時の私的注意ポイントは関数の実行ではなく、関数を渡す
ことです。
関数を渡す。
nozomiCard(dice);
関数を実行してしまう。
nozomiCard(dice());
関数の実行をしてしまうと、関数diceの実行結果(数字の1〜6)が引数として渡されてしまい、
想定している結果が得られません。
最初は関数を渡すということが違和感がある方は、
関数もオブジェクトの一種
で、値の一種
だ!!
という感覚を持ってください。
すんなり高階関数が理解できるようになると思います。
(一度関数をconsole.dirで確認すると理解が深まるかもしれません。)
よく使用する(よく見る)高階関数
よく使用する(よく見る)高階関数にforEach
, map
filter
, every
, some
, sort
, reduce
があります。
forEach
・コールバック関数を受け取り、配列の要素毎に関数を呼び出します。
forEach(function(element, index, array) { /* … */ }, thisArg)
element
現在処理されている配列の要素です。
index
配列内の element の添字です。
array
forEach() が呼び出されている配列です。
thisArg
省略可
コールバック関数内で this として使用する値です。
const nums = [2,4,6,8];
nums.forEach(function(element) {
console.log(element * element);
// 4, 8, 12, 16
});
個人的にはforEachよりもfor ofの方がよく使うような気がしてます。
map
・コールバック関数を配列要素毎に呼び出し、その結果からなる新しい配列を生成します。
map(function(element, index, array) { /* … */ }, thisArg)
element
配列内で現在処理中の要素です。
index
現在処理中の要素の配列内における添字です。
array
map が呼び出された配列です。
thisArg
省略可
コールバック関数を実行するときに this として使う値です。
const nums = [2,3,5,18];
const doubleNums = nums.map(function(element) {
return element * 2;
});
nums; //[2,3,5,18]
doubleNums; //[4,6,10,36]
forEachとの違いは、mapは新しい配列を生成します。
filter
・提供されたテスト関数を満たす要素から新しい配列を生成します。
filter(function(element, index, array) { /* … */ }, thisArg)
element
配列内で処理中の現在の要素です。
index
配列内で処理中の現在の要素の位置です。
array
filter() が呼び出された配列です。
thisArg 省略可
コールバック関数を実行するときに this として使用する値です。
conts nums = [1,2,3,4,5,6,7,8,9,10];
const evenNumber = nums.filter(function(element) {
return element % 2 === 0;
});
evenNumber; // [2,4,6,8,10]
提供されたコールバック関数はtrueかfalseを返します。
trueであればelementはfilterされた新しい関数(evenNumber)に追加されます。
filterなのでtrue を返した要素は残され、false を返した要素は取り除かれます。の方が正しいかも、、、。
every
・配列内の全ての要素
が指定されたテスト関数を満たすかどうかをtrue,falseで返す。
every(function(element, index, array) { /* … */ }, thisArg)
element
現在処理されている要素です。
index
現在処理されている要素の添字です。
array
every が実行されている配列です。
thisArg
省略可
コールバック関数を実行するときに this として使用すされる値です。
const nums = [2,4,5,7];
nums.every(function(element) {
return element % 2 === 0;
}); // false
const nums2 = [2,4,6,8];
nums2.every(function(element) {
return element % 2 === 0;
}); // true
some
・1つでもテスト関数を満たす要素があればtrueを返す。
some(function(element, index, array) { /* … */ }, thisArg)
element
配列内で現在処理中の要素です。
index
現在処理中の要素の配列内における添字です。
array
some が呼び出された配列です。
thisArg
省略可
コールバック関数を実行するときに this として使う値です。
const nums = [1,2,3,4,5];
nums.some(function(element) {
return element % 2 === 0;
}); // true
const nums2 = [1,3,5,7];
nums2.some(function(element) {
return element % 2 === 0;
}); // false
sort
・ソート順を定義する関数を指定し、辞書順にソートを行います。
・ソート順を定義する関数が省略された場合、各文字の Unicode コードポイント順に従ってソートされます。
sort(function compareFn(a, b) { /* … */ })
a
比較する第一要素。
b
比較する第二要素。
compareFn(a, b) の返値 | ソート順 |
---|---|
> 0 |
a を b の後に並べる |
< 0 |
a を b の前に並べる |
=== 0 |
a と b の元の順序を維持する |
let nums = [5,8,7,6];
let ascOrder = nums.sort(function(a, b){
return a - b; //昇順
});
ascOrder; //[5, 6, 7, 8]
let discOrder = nums.sort(function(a, b){
return b - a; //降順
});
discOrder; //[8, 7, 6, 5]
コピーは作成されず、ソートを実行した配列自体がソートされるため、注意が必要です。
reduce
・配列の各要素に対して(引数で与えられた)reducer関数を実行して「単一の出力値」を生成します。
reduce(function(previousValue, currentValue, currentIndex, array) { /* … */ }, initialValue)
previousValue
前回の callbackFn の呼び出し結果の値です。初回の呼び出しでは initialValue が指定されていた場合はその値、そうでない場合は array[0] の値です。
currentValue
現在の要素の値です。初回の呼び出しでは initialValue が指定された場合は array[0] の値であり、そうでない場合は array[1] の値です。
currentIndex
currentValue の位置です。初回の呼び出しでは、 initialValue が指定された場合は 0、そうでない場合は 1 です。
array
走査する配列です。
initialValue
省略可
コールバックが初めて呼び出されたときの previousValue の初期値です。
//配列内の合計値を求める
const nums = [3,5,6,7,8];
const result = nums.reduce(function(previousValue, currentValue){
return previousValue + currentValue;
});
result; //29
//配列内の最大値を求める
const maxNum = nums.reduce(function(max, currentValue){
if(max < currentValue) {
return currentValue;
}
});
maxNum; //8
上記の配列内の合計値を求める場合は、以下のようにreduceが実行されています。
Callback | previousValue | currentValue | return value |
---|---|---|---|
1回目 | 3 | 5 | 8 |
2回目 | 8 | 6 | 14 |
3回目 | 14 | 7 | 21 |
4回目 | 21 | 8 | 29 |
上記の配列内の最大値を求める場合は、以下のようにreduceが実行されています。
Callback | max | currentValue | return value |
---|---|---|---|
1回目 | 3 | 5 | 5 |
2回目 | 5 | 6 | 6 |
3回目 | 6 | 7 | 7 |
4回目 | 7 | 8 | 8 |
reduceはこれら以外にも様々な場面で使用することができます。
使い勝手はいいですが、私はまだまだ使いこなせていません泣
まとめ
・高階関数とは、「関数を受け取ったり関数を返す関数」のこと。
・関数もオブジェクトの一種で、値の一種だ!
参考
MND Web Docs
https://developer.mozilla.org/ja/