0
0

【JavaScript】高階関数について(コールバック、クロージャ、カリー化)

Last updated at Posted at 2024-08-10

はじめに

JavaScriptの高階関数などについて概要をまとめました。
概念的で難しく、かなり理解に苦しみました(まだ全て理解できていないと思います…)。
実際のプログラミングにおいて有効利用するにはもっと習熟しなければいけないと感じました。

高階関数

  • 関数を返す関数のこと
  • 関数をその他のオブジェクトと同様に扱うことで実現されている。このようなことができるプログラミング言語は、第一級関数を持つ、という

コールバック関数

  • 他の関数に引数として渡される関数のこと
  • 例としては、Array.prototype.map()、Array.prototype.forEach()、setTimeout()など

クロージャ

  • クロージャとは、それが内包する変数や関数などをまとめて閉じ込める役割を果たすものである
  • JavaScriptの関数は入れ子にすることができる
  • 内側の関数は、外側の関数のスコープ内に存在する変数にアクセスできる
  • 一方で、内側の関数には、外側の関数の中からしかアクセスできない
  • ゆえに、ある関数の内部は閉じられたひとつの範囲となるから、JavaScriptの関数はクロージャである
  • 次の例では、関数counterの中がまとめて閉じ込められることとなる。他のいくつかのプログラミング言語の感覚では、変数countは関数counterが呼び出された後に消滅するように見える。しかし、クロージャであるcounterは、変数countと関数countUpをまとめて閉じ込めるから、関数counterが生存する限り、変数countも生存することとなる
  • なお、「まとめて閉じ込める」とは、「内部の変数などをメンバとして持つ」という意味ではない。次の例において、関数countUpその内部で変数countを参照しているから、関数countUpが存在する限り変数countも「合わせてまとめて閉じ込められている」ということである
クロージャの例
function counter () {
  let count = 0;
  function countUp () {
    count += 1;
    return count;
  }
  return countUp;
}

const myCounter = counter();

for (let i = 0; i < 10; i++) {
  console.log(myCounter());
} // 1,2,3,4,5...,10と表示される。
  • データと関数を結びつけられるという点で、オブジェクト指向的な技術である。メソッドを一つだけ持つオブジェクトのようなものである

クラス

20240811 コメントにて教示いただいた内容を元に追記

  • データと関数をひとつにまとめるならば、ES2015以降ではクラスを用いることができる
クラスの例
class MyClass {
    constructor(x) {
        this.x = x;
    }

    incrementX() {
        this.x++;
    }

    get x() {
        return this.x;
    }

    set x(value) {
        this.x = value;
    }
}

const myClassA = new MyClass(1);

カリー化

カリー化の例
function currying(x) {
    return function(y) {
        return x + y;
    };
}

const currying5 = currying(5);
const currying10 = currying(10);

console.log(currying5(2)); //7
console.log(currying10(2)); //12
  • 上の例の関数curryingは、カリー化しないのならば下の例のようにも書ける
カリー化しない例
function notCurrying(x, y) {
    return x + y;
}

console.log(notCurrying(5, 2)); //7
console.log(notCurrying(10, 2)); //12
  • なぜカリー化するかというと、関数を部分的に適用することができるからである。前者の例における次の部分で、いわば新しい関数を作成している
部分適用
const currying5 = currying(5);
const currying10 = currying(10);
  • クロージャによって、内部の関数が一つ目の引数を保持するため、このようなことができる
  • ちなみに、次のようにも呼び出せる
別の呼び方
currying(2)(5);
  • アロー関数を用いる場合は次のようになる
アロー関数を用いたカリー化の例
const currying = x => y => x + y;

関数の合成

  • 関数型プログラミングでは、小さな関数(純粋関数であるという条件付き)を定義し、それらの関数を合成してプログラミングをする
簡素な関数の合成の例
const compose = (f1, f2) => x => f1(f2(x));

this

  • thisは、呼び出し元によって動的に参照先が変わる。ゆえに、関数の定義だけを見てthisの値が何かを決定できない
  • 基本的な参照先はベースオブジェクト(メソッドを呼ぶ際に、そのメソッドのドット演算子またはブラケット演算子のひとつ左にあるオブジェクトのこと)となる
  • ただし、アロー関数内で定義された関数やメソッドにおけるthisがどの値を参照するかは関数の定義時に決まる
  • アロー関数内のthisの参照は、外側のスコープへと探索していき、見つかったところのthisを示す
  • また、Function.prototype.bind()によって、引数に指定したオブジェクトをthisが参照するようにできる
    20240811 コメントにより教示いただいた内容を元に追記
  • クラスにおいて、thisは、newによって生成されたインスタンスを指す
  • constructor関数におけるthisは、インスタンスを示すオブジェクトである

参考文献

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0