JavaScriptの高次関数を深く勉強します。


高次関数
高次関数は英語でHigher-order functionと言います。その定義は簡単です。以下の条件を満たす関数です。
  • は、入力
  • として1つ以上の関数を受け取る。
  • は、関数
  • を出力します。
    すなわち高次関数は他の関数を動作させる関数であり、それらをパラメータとして伝えるか、あるいはそれらを返すことができる。簡単には高次関数はパラメータとしての受信関数、または戻り値出力の関数としての関数です。
    関数をパラメータとして伝える
    JavaScript言語には、Aray.prototype.map、Aray.prototype.filterとAray.prototype.reduceのような高次関数がいくつか内蔵されています。これらはパラメータとして関数を受け取り、リストの各要素にこの関数を適用します。高次関数を用いないスキームとの比較を見てみた。
    Aray.prototype.map
    map()メソッドは、供給された関数を呼び出して返された結果、元の配列が変更されない新しい配列を作成します。mapに渡すコールバック関数は、3つのパラメータを受け取ります。それぞれ、currentValue、index、arrayです。calback以外にも、calback関数を実行する際に使用するthis値を受け入れることができます。
    簡単な例では分かりやすいですが、現在は[1,2,3,4]の配列があります。新しい配列を生成したいです。各要素は前の配列の二倍です。高次関数と高次関数を使用しないで次の2つの方法があります。
    高次関数を使用しない
    
    //    
    const arr1 = [1, 2, 3, 4];
    const arr2 = [];
    for (let i = 0; i < arr1.length; i++) {
    arr2.push( arr1[i] * 2);
    }
    console.log( arr2 );
    // [2, 4, 6, 8]
    console.log( arr1 );
    // [1, 2, 3, 4]
    高次関数を使う
    
    //    
    const arr1 = [1, 2, 3, 4];
    const arr2 = arr1.map(item => item * 2);
    console.log( arr2 );
    // [2, 4, 6, 8]
    console.log( arr1 );
    // [1, 2, 3, 4]
    Aray.prototype.filter
    filter()法は関数を提供することによって実現されるテストを含む新しい配列を作成し、元の配列は変化しない。受信したパラメータは、mapと同じで、その戻り値は新しい配列であり、テストを通してすべての要素から構成されています。テストを通過する配列要素がなければ、空の配列を返します。
    この例を紹介します。現在は「1、2、1、2、3、5、5、5、3、4、4、4、4」の配列があります。この配列は重複した内容がない、つまり重さがないという新しい配列を生成したいです。
    高次関数を使用しない
    
    const arr1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
    const arr2 = [];
    for (let i = 0; i < arr1.length; i++) {
    if (arr1.indexOf( arr1[i] ) === i) {
    arr2.push( arr1[i] );
    }
    }
    console.log( arr2 );
    // [1, 2, 3, 5, 4]
    console.log( arr1 );
    // [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]
    高次関数を使う
    
    const arr1 = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
    const arr2 = arr1.filter( (element, index, self) => {
    return self.indexOf( element ) === index;
    });
    console.log( arr2 );
    // [1, 2, 3, 5, 4]
    console.log( arr1 );
    // [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]
    Aray.prototype.reduce
    reduce()方法は、配列内の各要素に対して提供されるreducer関数(昇順実行)を実行し、その結果を単一の戻り値に纏める。reduceに渡されるコールバック関数は、アキュムレータaccumultor、currentValue、current Index、arrayの4つのパラメータを受け取り、calbackの他に、初期値initialValueの値を受け入れることができます。
  • はinitial Valueを提供していない場合、初めてcalback関数を呼び出すとき、accumultorは元の配列の最初の要素を使用して、currentValueは配列の中の第二の要素です。初期値のない空配列でreduceを呼び出したらエラーが発生します。
  • にinitial Valueが提供されると、calback関数を呼び出す最初のパラメータの値であるaccumultor、currentValueは、元の配列の最初の要素を使用する。
  • 簡単な例を紹介しますが、現在は配列[0,1,2,3,4]があります。配列要素の和を計算する必要があります。需要は比較的簡単で、コードの実現を見てください。
    高次関数を使用しない
    
    const arr = [0, 1, 2, 3, 4];
    let sum = 0;
    for (let i = 0; i < arr.length; i++) {
    sum += arr[i];
    }
    console.log( sum );
    // 10
    console.log( arr );
    // [0, 1, 2, 3, 4]
    高次関数を使う
    initial Value値なし
    
    const arr = [0, 1, 2, 3, 4];
    let sum = arr.reduce((accumulator, currentValue, currentIndex, array) => {
    return accumulator + currentValue;
    });
    console.log( sum );
    // 10
    console.log( arr );
    // [0, 1, 2, 3, 4]
    上にはinitial Valueがない場合、コードの実行過程は以下の通りで、calbackは全部で4回呼び出します。
    calback
    accumullator
    current Value
    currentIndex
    array
    return value
    ファーストコール
    0
    1
    1
    [0,1,2,3,4]
    1
    second call
    1
    2
    2
    [0,1,2,3,4]
    3
    third call
    3
    3
    3
    [0,1,2,3,4]
    6
    fourth call
    6
    4
    4
    [0,1,2,3,4]
    10
    initial Value値があります
    私たちはまた、initial Valueがある場合には、initial Value値が10であると仮定して、コードを確認します。
    
    const arr = [0, 1, 2, 3, 4];
    let sum = arr.reduce((accumulator, currentValue, currentIndex, array) => {
    return accumulator + currentValue;
    }, 10);
    console.log( sum );
    // 20
    console.log( arr );
    // [0, 1, 2, 3, 4]
    コードの実行過程は以下の通りです。コールバックは全部で5回呼び出します。
    calback
    accumullator
    current Value
    currentIndex
    array
    return value
    ファーストコール
    10
    0
    0
    [0,1,2,3,4]
    10
    second call
    10
    1
    1
    [0,1,2,3,4]
    11
    third call
    11
    2
    2
    [0,1,2,3,4]
    13
    fourth call
    13
    3
    3
    [0,1,2,3,4]
    16
    fth call
    16
    4
    4
    [0,1,2,3,4]
    20
    関数を戻り値として出力します。
    これはよく分かります。一つの関数を返して、二つの例を直接見て理解を深めます。
    isType関数
    タイプを判断するときは、Object.prototype.toString.callを通じて、対応するオブジェクトから返される文字列を取得することができることを知っています。
    
    let isString = obj => Object.prototype.toString.call( obj ) === '[object String]';
    let isArray = obj => Object.prototype.toString.call( obj ) === '[object Array]';
    let isNumber = obj => Object.prototype.toString.call( obj ) === '[object Number]';
    上の3行のコードには多くの重複コードがあることが分かります。具体的なタイプを抜き出すだけで判断タイプの方法にパッケージすることができます。コードは下記の通りです。
    
    let isType = type => obj => {
    return Object.prototype.toString.call( obj ) === '[object ' + type + ']';
    }
    isType('String')('123'); // true
    isType('Array')([1, 2, 3]); // true
    isType('Number')(123); // true
    ここでは、isType関数は、Obj=>{...}という関数を返す値として出力する高次関数です。
    add関数
    よくある面接問題を見て、JSで無限の累積関数addを実現します。例は以下の通りです。
    
    add(1); // 1
    add(1)(2); // 3
    add(1)(2)(3); // 6
    add(1)(2)(3)(4); // 10 
    //     
    構造と上のコードは少し似ていますが、いずれも関数を戻り値として出力し、新しいパラメータを受信して計算します。
    印刷関数が自動的にtoString()メソッドを呼び出すことを知っています。関数add(a)は一つのクローズドsum(b)を返します。関数sum()の中でアキュムレータ計算a=a+bはsum.toString()を書き換えるだけで変数aに戻ります。
    
    function add(a) {
    function sum(b) { //     
    a = a + b; //   
    return sum;
    }
    sum.toString = function() { //   toString()  
    return a;
    }
    return sum; //       
    }
    add(1); // 1
    add(1)(2); // 3
    add(1)(2)(3); // 6
    add(1)(2)(3)(4); // 10 
    以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。