JavaScriptの第一級関数と高階関数とは何かについてとそれらの違い

2021年2月4日JavaScript

JavaScriptの第一級関数と高階関数とは何かについてとそれらの違いについてまとめました。

 

JavaScriptの第一級関数(first-class functions)

JavaScriptは関数を第一級関数(functions as first-class citizens)として扱います。
どういう意味かというと、単に値として扱うという意味です。
言い換えると、JavaScriptでは関数は、オブジェクト型となります。
オブジェクトは値であり、関数も値となります。
関数が値なので、たとえば変数に値を格納することもできます。

const add = (x, y) => x + y;

const foo = {
  value: 10,
  method: function() { this.value++; }
}

また、関数を他の関数に引数として渡すことができます。

const hello = () => console.log('Hello');
a.addEventListener('click', hello)

さらに、関数から関数を返すこともできます。(bindメソッドなど)

JavaScriptの高階関数(Higher-Order Functions)

この「JavaScriptは関数を第一級関数として扱う」ということが、JavaScriptの高階関数を可能にしています。
JavaScriptの高階関数(Higher-Order Functions)は、引数として他の関数を受け取る関数や、新しい関数を返す関数、またはその両方を行う関数のことです。

他の関数を受け取る関数の例

const hey = () => console.log('hey');
a.addEventListener('click', hey)

この「addEventListener」は高階関数で、「hey」がコールバック関数です。
高階関数であるaddEventListenerが、heyに対して「クリックされるまで動かないでね」と指示を出しているかのように見えます。
コールバック関数の具体例と他の関数を受け取る関数の具体例については、下の「高階関数とコールバック関数」を参照してみてください。

新しい関数を返す関数の例

function print() {
//新しい関数を返す関数の例

const hello = function(greeting) {
  return function(name) {
    console.log(`${greeting}、${name}さん`);
  }
}
const greet = hello('こんにちは');
greet('山田');// こんにちは、山田さん
greet('小枝');// こんにちは、小枝さん
greet('天童');// こんにちは、天童さん

hello('こんにちは')('斎藤');// こんにちは、斎藤さん

//上の関数をアロー関数に書き換えるとこのようになる。
const hello2 = greeting => name => console.log(`${greeting}、${name}さん`);
hello2('こんにちは')('斎藤');// こんにちは、斎藤さん

これは、function print(){}が高級関数で、その中のreturn function(){}を明らかに返しています。

JavaScriptの第一級関数と高階関数の違い

第一級関数というのは、他の言語にもあったりなかったりする機能のひとつです。
第一級関数は、「関数は値だよ」というただのコンセプトです。

それに対し、高階関数は、第一級関数というコンセプトがあるから生まれる機能です。

 

高階関数とコールバック関数

JavaScriptでは、コールバック関数を多用します。
それを可能にしているのが、高階関数です。
その具体例をもう少し詳しくみてみましょう。
まずは以下の例をみてみましょう。

//他の関数を受け取る関数

const oneWord = function(str) {
  return str.replace(/ /g, '').toLowerCase();
}

const upperFirstWord = function(str) {
  const [first, ...others]= str.split(' ');
  return [first.toUpperCase(), ...others].join(' ');
}

//高階関数
const transformer = function(str, fn) {
  console.log(`文字列変換後: ${fn(str)}`);
  console.log(`文字列変換 by : ${fn.name}`);
}

transformer('Which one do you like, JavaScript or Java', upperFirstWord);
  // 文字列変換後: WHICH one do you like, JavaScript or Java
  // 文字列変換 by : upperFirstWord
transformer('Which one do you like, JavaScript or Java', oneWord);
  // 文字列変換後: whichonedoyoulike,javascriptorjava
  // 文字列変換 by : oneWord

上の高階関数であるtransformerに文字列と関数を渡して、その文字列のなんらかの処理シてもらいたいという内容です。
そのとき、upperFirstWordは関数ですが、ただの値として渡されています。upperFirstWord()となっていないため、呼び出しをしているわけではないのがわかるでしょう。
また、関数はメソッドまでもっています。その例として、nameメソッドが上の例に使用しています。
このupperFirstWordはコールバック関数と呼ばれます。「あとで呼んでね(Call back later)」といっているようなもので、高階関数であるtransformerが、「今だよ」というときに動作します。
addEventListerもその代表ですし、forEachにもコールバック関数をいれることができます。
これが便利なところは、関数を分けられるようになるため、抽象化できます。その結果、再利用性が高まります。メンテナンスもしやすくなります。