深入浅出原生jsの高次関数

10432 ワード

高次関数
高次関数は何ですか?
JSの関数はいずれも変数を指しています.変数は関数を指し、関数のパラメータは変数を受け取ることができるので、もう一つの関数はパラメータとして受け取ることができます.この関数は高次関数と呼ばれます.
一番簡単な高次関数:
function add(x,y,f){
    return f(x)+f(y);
}
add(-5,6,Math.abs)を呼び出すと、パラメータx,yとfはそれぞれ-5,6と関数Math.absを受信し、関数の定義に従って、計算プロセスを導き出すことができます.
x = -5;
y = 6;
f = Math.abs;
f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;
return 11;
高次関数を作成するという意味は、関数のパラメータが他の関数を受信できるようにすることです.
map/reduce
map
map()メソッド:元の配列の要素ごとに指定方法を呼び出した後、戻り値からなる新しい配列を返します.
例えば、私たちは関数f(x)=x 2を持っています.この関数を行列[1,2,3,4,5,6,7,8,9]に作用させるには、下記のようにmapで実現できます.
map()の方法はJavaScriptのArayで定義されているので、Arayのmap()のメソッドを呼び出して、自分たちの関数を入力して、結果として新しいArayを得ました.
function pow(x){ //         return x*x; } var arr=[1,2,3,4,5,6,7,8,9]; var result = arr.map(pow); //map()           console.log(result); //  :[1,4,9,16,25,36,49,64,81];
なお、map()が入ってくるパラメータはpowであり、関数オブジェクト自体です.
map()は高次関数として、実は演算規則を抽象化しています.
reduce
reduce()方法:配列内の各要素に対して、順番に調整関数を実行します.(配列中の削除または割り当てられていない要素を含みません.)具体的な結果を返します.
reduce文法:
arr.reduce(callback,[initialValue])
  • calback(実行行列内の各値関数は、4つのパラメータを含む)
  • previous Value(第一項の値または前回の重畳の結果値、または提供された初期値)
  • index(現在の要素の配列内のインデックス)
  • array(配列自体)
  • initialValue(初めてコールされたcalbackの最初のパラメータとして、戻り値を制御できるフォーマット)
  • filter
    filterは、Arayのいくつかの要素をフィルタリングし、残りの要素を返すためによく使われています.map()と同様に、Arayのfilter()も関数を受信します.map()とは異なり、filter()は、入力された関数を順番に各要素に作用させます.そして、戻り値によってtrueかfalseかを決定します.
    例えば、一つのArayでは偶数を削除して奇数だけ残してもいいです.
    var arr=[1,2,4,5,6,9,10,15];
    var r= arr.filter(function (x){
        return x%2!==0;
    });
    r;//[1,5,9,15]
    
    Arayの空の文字列を削除します.このように書くことができます.
    var arr = ['A','','B',NULL,undefined,'C',' '];
    var r = arr.filter(function (s){
        return s&& s.trim();//  :IE9      trim()  
    });
    r;//['A','B','C']
    
    filter()という高次関数を使って、正確に'スクリーニング'関数を実現することが鍵となります.
    コールバック関数
    filter()が受信したコールバック関数は、実際には複数のパラメータを持つことができます.通常は最初のパラメータのみを使用して、アラyのある要素を表します.コールバック関数はまた、要素の位置と配列自体を表します.
    var arr=['A','B','C'];
    var r=arr.filter(function (element,index,self){
        console.log(element);//    'A','B','C'
        console.log(index);//    0,1,2
        console.log(self);self    arr
        return true;
    });
    
    filterを利用して、Arayの重複要素を巧みに除去することができます.
    'use strict';
    var r,
    arr=['apple','strawberry','banana','pear', 'apple', 'orange', 'orange', 'strawberry']
    
    r=arr.filter(function(element,index,self){
        return self.indexOf(element)==index;
    });
    console.log(r.toString());
    
    重複要素の除去に依存するのはindexOfであり、常に最初の要素の位置に戻り、後続の重複要素はindexOfの戻り位置と等しくないので、filterによってフィルタリングされる.
    ソートアルゴリズム
    sort()方法原理
    jsの中のsort方法はjavaのsoft方法と違って、標準的には昇順に配列項目-最小の列を一番前に並べて、一番大きいのは一番後ろに並べます.しかし、jsでは、sort方法は各配列項目のtoString()の変換方法を呼び出して、その後に比較した文字列はどのように並べ替えられますか?配列の中でも、sort(方法)の比較も文字列です.
    var values=[0.1.5.10.15];
    values.sort();
    alert(values);//0.1.10.15.5
    
    デフォルトでは、文字列の並べ替えはASCLLのサイズによって比較されます.
    sort()方法は直接Arayを修正します.その結果はやはり現在のArayです.
    Aray
    配列に関しては、map().reduce.filter().sort()のような方法が関数に導入されるほか、Arayオブジェクトは非常に実用的な高次関数を多く提供する.
    every
    every()メソッドは、配列のすべての要素が試験条件を満たしているかどうかを判断することができます.
    例えば、複数の文字列を含む配列を指定して、すべての文字列が指定された試験条件を満たしているかどうかを判定します.
    'use strict';
    var arr = ['Apple', 'pear', 'orange'];
    console.log(arr.every(function (s) {
        return s.length > 0;
    })); // true,          s.length>0
    
    console.log(arr.every(function (s) {
        return s.toLowerCase() === s;
    })); // false,               
    
    find
    find()メソッドは、条件に合致する最初の要素を検索するために使用されます.見つかったら、この要素を返します.そうでなければ、undefinedを返します.
    'use strict'
    var arr = ['Apple', 'pear', 'orange'];
    console.log(arr.find(function (s) {
        return s.toLowerCase() === s;
    })); // 'pear',   pear     
    
    console.log(arr.find(function (s) {
        return s.toUpperCase() === s;
    })); // undefined,             
    
    findIndex
    findIndex()とfind()は同様で、条件に合う最初の要素を検索するのです.違いはfindIndex()がこの要素の索引を返します.見つけられなかったら、-1を返します.
    'use strict'
    var arr = ['Apple', 'pear', 'orange'];
    console.log(arr.findIndex(function (s) {
        return s.toLowerCase() === s;
    })); // 1,   'pear'    1
    
    console.log(arr.findIndex(function (s) {
        return s.toUpperCase() === s;
    })); // -1
    
    forEach
    forEach()とmap()は同様で、各要素を順次に導入された関数に作用させますが、新しい配列には戻りません.forEach()は遍歴配列でよく使われていますので、入力された関数は戻り値を必要としません.
    'use strict';
    var arr = ['Apple', 'pear', 'orange'];
    arr.forEach(console.log); //         
    
    包みを閉じる
    関数を返します.
    高次関数は、パラメータとして関数を受け入れるほか、結果値として関数を返します.
    例:Arayに対する求和を実現する.一般的に、求和の関数はこのように定義される.
    function sum(arr) {
    return arr.reduce(function (x, y) {
        return x + y;
    });
    }
    
    sum([1, 2, 3, 4, 5]); // 15
    
    しかし、すぐに和を求める必要がないなら、後のコードの中で、必要に応じて計算したらどうなりますか?求和の結果を返さずに、求和の関数を返してもいいです.
    function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
    }
    
    lazy_を呼び出したらsumの場合、戻りは求和結果ではなく、求和関数です.
    var f=lazy_sum([1,2,3,4,5]);//function sum()
    
    関数fを呼び出した時、本当に合計と結果を計算します.
    f();//15
    
    この例では、関数のlazy_にいます.sumには関数sumが定義されています.また、内部関数sumは外部関数lazy_を参照することができます.sumのパラメータと局所変数は、lazy_sumが関数sumに戻るとき、関連パラメータと変数は戻りの関数に保存されます.このような「クローズド」と呼ばれるプログラム構造は非常に威力があります.
    注意したいのは、lazy_を呼び出した時です.sum()を呼び出すたびに、同じパラメータが入ってきても、新しい関数が戻ります.
    var f1 = lazy_sum([1, 2, 3, 4, 5]);
    var f2 = lazy_sum([1, 2, 3, 4, 5]);
    f1() f2()         .
    
    閉包とは何ですか
    閉包とは何ですか
    コードセグメント1
    function a(){
    var n = 0;
    function inc() {
        n++;
        console.log(n);
    }
    inc();  
    inc(); 
    }
    a(); //     1,   2
    
    コードセグメント2
    function a(){
    var n = 0;
    this.inc = function () {
        n++; 
        console.log(n);
    };
    }
    var c = new a();
    c.inc();    //     1
    c.inc();    //     2
    
    クローズド:別の関数にアクセスする権限があります.スコープ内の変数の関数はすべてクローズドです.
    なお、関数名は1つの識別子(関数へのポインタ)であり、()は実行関数である.
    閉じたパケットを作成する方法は、関数の内部に別の内部(プライベート)関数を作成します.
    締め括りをつける
    クローズドとは、関数が別の関数の変数を参照することです.変数が参照されているので回収されないので、プライベート変数をカプセル化することができます.これは利点であり、欠点でもあります.不要なクローズドはメモリ消費量を増やすだけです.また、クローズドパックを使う場合も、変数の値があなたの要求に合致するかどうかに注意します.彼は静的なプライベート変数のようです.
    矢印関数
    ES 6標準には新しい関数が追加されました.アルロW Function(矢印関数)はなぜアルロW Functionと呼ばれていますか?彼の定義は矢印であるからです.
    x=>x*x
             :
    function(x){
        return x*x;
    }
    
    矢印関数は匿名関数に相当し、関数定義を簡略化しました.矢印関数には上のような2つのフォーマットがあります.上のように、1つの表現だけが含まれています.「...」と「return」も省略されました.もう一つは複数の文が含まれています.この場合は省略できません.
    x=>{
        if(x>0){
            return x*x;
        }
        else{
            return -x*x;
        }
    }
    
    パラメータが一つでない場合は、括弧()で囲む必要があります.
    //    :
    (x,y)=>x*x +y*y
    
    //   :
    ()=>3.14
    
    //    :
    (x,y,...rest)=>{
        var i,sum=x+y;
        for(i=0;i
    オブジェクトを返す場合は、単表現であればエラーが発生します.
    //SyntaxError:
    x=>{foo:x}
    
    関数の「...」と文法的に衝突していますので、次のように変更します.
    //ok:
    x=>({foo:x})
    
    this
    矢印関数は匿名関数の簡単な書き込みのように見えるが、矢印関数と匿名関数には明確な違いがある.矢印関数の内部のthisは語法のスコープであり、コンテキストによって決定される.
    JS関数によるthisバインディングのエラー処理のため、以下の例は予想される結果を得ることができませんでした.
    var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = function () {
            return new Date().getFullYear() - this.birth; // this  window undefined
        };
        return fn();
    }
    };
    
    現在、矢印関数は完全に修復されたthisの指向、thisはいつも語法の作用域、つまり外層調合者objを指しています.
    var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this  obj  
        return fn();
    }
    };
    obj.getAge(); // 25
    
    矢印関数を使うと、以前のようなhackの書き方があります.
    var that = this;
    
    もう必要ではないです.矢印関数ではthisは語法によってドメインが結合されていますので、call()またはappy()で矢印関数を呼び出すと、thisをバインディングできません.つまり、最初のパラメータが無視されます.
    var obj={
        birth:1990,
        getAge:function(year){
            var b=this.birth;//1990
            var fn=(y)=>y-this.birth;//this.birth;//this.birth  1990
            return fn.call({birth:2000},year);
        }
    };
    obj.getAge(2015);//25
    
    ゲナート
    generatorはES 6規格で導入された新しいデータタイプです.一つのgeneratorは関数のように見えますが、複数回戻ります.
    一つの関数は完全なコードで、一つの関数を呼び出したら着信パラメータです.そして結果を返します.
    function foo(x){
        return x+x;
    }
    var r = foo(1);//  foo  
    
    関数が実行中にreturn文に遭遇していない場合(関数の末尾にreturnがないと暗黙のreturn undefinedとなります.)、制御権は呼び出されたコードに返送できません.
    ゲナートは関数と似ています.定義は以下の通りです.
    function * foo(x){
        yield x+1;
        yield x+2;
        return x+3;
    }
    
    generatorと関数が違っているのは、generatorがfunctionで定義されています.また、return文以外にも、yieldで何回も返すことができます.
    例えば、有名なフィボナッチの数を例に挙げて、0,1から始まる:
    0 1 1 2 3 5 8 13 21 34 ...
    
    フィボナッチの数列を生成する関数を作成するには、次のように書くことができます.
    function fib(max) {
    var
        t,
        a = 0,
        b = 1,
        arr = [0, 1];
    while (arr.length < max) {
        [a, b] = [b, a + b];
        arr.push(b);
    }
    return arr;
    }
    
    //   :
    fib(5); // [0, 1, 1, 2, 3]
    fib(10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
    
    関数は一回しか戻ってこないので、一つのアレに戻らなければなりません.しかし、ゲナートに変えると、一度に一つの数を返して、何度も何回も戻ります.ゲナートで次のように書き換えます.
    function * fib(max){
        var t,
            a= 0,
            b= 1,
            n= 0;
        while (n
    直接呼び出してみます
    fib(5); // fib {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}
    
    直接に一つのgeneratorを呼び出すのと関数が違っています.fib(5)は一つのgeneratorオブジェクトを作成しただけで、まだ実行していません.
    generatorオブジェクトを呼び出すには二つの方法があります.一つは常にgeneratorオブジェクトのnextを呼び出す方法です.
    var f = fib(5);
    f.next(); // {value: 0, done: false}
    f.next(); // {value: 1, done: false}
    f.next(); // {value: 1, done: false}
    f.next(); // {value: 2, done: false}
    f.next(); // {value: 3, done: false}
    f.next(); // {value: undefined, done: true}
    
    next()方法はgeneratorのコードを実行します.そして、毎回yield xに出会います.一つのオブジェクトに戻って、その後「一時停止」します.戻ってきたvalueはyieldの戻り値です.doneはこのgeneratorがすでに実行終了したかどうかを表しています.doneがtrueであれば、valueはreturnの戻り値です.
    doneがtrueで実行されると、このgeneratorのオブジェクトはすでに全部実行済みです.これ以上呼び出さないでください.==next()=です.
    二つ目の方法は直接for…ofサイクル反復generatorオブジェクトを使うことです.この方法は私達自身がdoneを判断する必要がありません.