JAvascript-call、apply、bindメソッドの理解

6064 ワード

1.紹介
フロントエンドの仕事を探しに出たばかりの頃、最も一般的な面接問題は「callとapplyに対する理解を話してください」で、以前はこれらの名詞しか知らなかったが、少しも理解していなかった.jqueryの熟知に伴いjqueryソースコードの多くがapplyメソッドを用いていることが判明し,ついでにいくつかの機能に類似したcallとbindメソッドの使用をまとめた.
JavaScriptの各Functionオブジェクトのプロトタイプにはapply()メソッドとcall()メソッドがあり、callとapplyはいずれもある関数の実行時のcontextであるコンテキストを変更するために存在し、言い換えれば、関数体内のthisの方向を変更するためである.callとapplyはthisを動的に変更するために現れ、1つのオブジェクトに方法がないが、他のオブジェクトがある場合は、callまたはapplyを使用して他のオブジェクトの方法で操作することができます.bindメソッドは、現在の関数が実行するコンテキストを変更するためにも使用できます.call、applyとは異なり、bindは対応する関数を返し、後で呼び出すのに便利ですが、apply、callはすぐに呼び出されます.
1.1関連定義:
  • call()構文:fun.call([thisObj[,arg1[, arg2[, [,.argN]]]]])定義:現在のオブジェクトを別のオブジェクトに置き換えるオブジェクトのメソッドを呼び出します.説明:callメソッドは、別のオブジェクトの代わりにメソッドを呼び出すために使用できます.callメソッドでは、関数のオブジェクトコンテキストを最初のコンテキストからthisObjによって指定された新しいオブジェクトに変更できます.thisObjパラメータが指定されていない場合、GlobalオブジェクトはthisObjとして使用されます.
  • apply()構文:fun.apply([thisObj[,argArray]])定義:現在のオブジェクトを別のオブジェクトに置き換えるオブジェクトのメソッドを適用します.説明:argArrayが有効な配列でないかargumentsオブジェクトでない場合、TypeErrorが発生します.argArrayとthisObjのいずれかのパラメータが指定されていない場合、GlobalオブジェクトはthisObjとして使用され、パラメータは伝達されません.
  • bind()構文:fun.bind(thisArg[, arg1[, arg2[, ...]]])定義:新しい関数を作成し、呼び出されたときにthisキーワードを提供する値に設定し、新しい関数を呼び出す前に所定のパラメータシーケンスを提供することができる.説明:パラメータarg 1,arg 2,...バインド関数が呼び出されると、これらのパラメータは実パラメータの前にバインドされたメソッドに渡されます.これらのパラメータは、ターゲット関数のパラメータリストの開始位置に挿入され、バインド関数に渡されるパラメータが後に続きます.bindは、指定したthis値と初期化パラメータによって改造された元の関数コピーを返します.

  • 1.2差異:
  • callおよびapply:call()メソッドは、いくつかのパラメータのリストを受け入れ、apply()メソッドは、複数のパラメータを含む配列を受け入れる.
  • bindとcall、apply:bindは対応する関数を返し、後で呼び出すのに便利で、apply、callはすぐに呼び出されます.

  • 1.3適用シーン:
  • オリジナルオブジェクトを使用する方法
  • 継承
  • を実現
  • bind法の簡単な使用
  • 2.使用
    2.1オリジナルオブジェクトを使用する方法
    (1)クラス配列Array原生法を用いる
    let arrayLike = {0: "hello", 1: "sunny", 2: "~", length: 3};
    let arrayLikeToArray1_1 = Array.prototype.slice.call(arrayLike); // ["hello", "sunny", "~"]
    let arrayLikeToArray1_2 = Array.prototype.slice.call(arrayLike, 0, 2); // ["hello", "sunny"]
    let arrayLikeToArray2_1 = Array.prototype.slice.apply(arrayLike); // ["hello", "sunny", "~"]
    let arrayLikeToArray2_2 = Array.prototype.slice.apply(arrayLike, [0, 2]); // ["hello", "sunny"]
    

    (2)Arrayオブジェクト属性を拡張し,クラス配列もこれらの静的メソッドを用いてテストできるようにするときに一部の属性のみを拡張し,他に必要なものがあれば自分で拡張するよ~~~メソッドは類似している.
    let array1 = ["hello", "sunny", "~"];
    Array.join = function (a, sep) {
    
        // return Array.prototype.join.call(a, sep);
        return Array.prototype.join.apply(a, [sep]);
    };
    Array.slice = function (a, start, end) {
    
        // return Array.prototype.slice.call(a, start, end);
        return Array.prototype.slice.apply(a, [start, end]);
    };
    Array.map = function (a, callback, thisArg) {
    
        // return Array.prototype.map.call(a, callback, thisArg);
        return Array.prototype.map.apply(a, [callback, thisArg]);
    };
    console.log(Array.join(array1, "-")); // hello-sunny-~
    console.log(Array.slice(array1, 0, 1)); // ["hello"]
    Array.map(array1, function (value, index) {
    
        console.log("index=" + index + ",value=" + value);
    }); // index=0,value=hello index=1,value=sunny index=2,value=~
    

    説明:callもapplyも拡張可能ですね.注釈を落とす方法も可能です.
    2.2継承の実装
    子クラスは親クラスのメソッドを使用します.厳密には継承ではなく、他のオブジェクトを呼び出すメソッド(このメソッド内のthisは使用者を指します).
    function Animal(name) {
    
        this.name = name || "Animal";
        this.showName = function() {
    
            console.log(this.name);
        }
    }
    function Cat(name) {
    
        this.name = name || "Cat";
    }
    let animal = new Animal();
    let cat1 = new Cat();
    let cat2 = new Cat("cat2");
    //          ,                   
    animal.showName.call(cat1); // Cat
    animal.showName.apply(cat1); // Cat
    animal.showName.call(cat2); // cat2
    animal.showName.apply(cat2); // cat2
    

    2.3 bindメソッドの簡単な使用
    説明:実際の開発ではbindの使用はあまり見られなかったので、簡易なdemoを2つしか作りませんでした.bindのより高度な使い方については、皆さん自身で模索する必要がありますが、私の理解はここに限られています.(1)バインド関数の作成
    window.name = "window";
    let user = {
        name: "user",
        getName: function() {
    
            console.log(this.name);
        }
    };
    user.getName(); // user
    let getUserName1 = user.getName; // this       
    getUserName1(); // window
    let getUserName2 = user.getName.bind(user);
    getUserName2(); // user
    

    (2)バイアス関数
    function add() {
    
        let sum = 0;
        for (let i = 0, len = arguments.length; i < len; i++) {
    
            sum += arguments[i];
        }
        console.log(this + ":sum=" + sum);
    }
    let add1 = add.bind("add1", 2, 3);
    add1(); // add1:sum=5
    add1(5); // add1:sum=10
    let add2 = add.bind("add2", "hello");
    add2(); // add2:sum=0hello
    add2(" sunny"); // add2:sum=0hello sunny
    

    3.拡張
    3.1クラス配列
  • クラス配列は、Arrayコンストラクション関数によって作成されたオブジェクトではありませんが、配列の動作を示します.クラス配列オブジェクトには、クラス配列オブジェクトに(call、applyメソッドを使用して)配列の操作方法を適用できる特性があります.
  • ブラウザ環境でdocument.getElementsByTagName()文はクラス配列オブジェクトを返します.function呼び出しでは、functionコード内のarguments変数(受信パラメータを保存)もクラス配列オブジェクトです.ECMAScript 5規格では、文字列stringは読み取り専用のクラス配列オブジェクトです.

  • いくつかの一般的なクラス配列:
    let obj = {0: "hello", 1: "sunny", 2: "~", length: 3};
    let anchor = document.getElementsByTagName("a");
    let msg = "hello";
    function test() {
    
        console.log(arguments); // arguments
    }
    

    3.2コリー化
  • 以前に関数式プログラミングを見たときに知ったことがありますが、n個のパラメータがある関数をn個のパラメータが1個しかない関数に変えるという意味です.コリー化の簡単な実現:
  • //     
    function sum1(x, y, z) {
    
        console.log("sum1:x+y+z=" + (x + y + z));
    }
    sum1(1, 3, 4); // sum1:x+y+z=8
    //        
    function sum2(x) {
    
        return function (y) {
    
            return function (z) {
    
                console.log("sum2:x+y+z=" + (x + y + z));
            }
        }
    }
    sum2(1)(3)(4); // sum2:x+y+z=8
    

    3.3バイアス関数
  • バイアス関数は、関数の1つまたは複数のパラメータを固定し、新しい関数を返し、残りのパラメータを受信し、パラメータの個数は1つでも2つでも、さらに多くてもよい.具体例はbindメソッドの単純な使用-demo 2
  • を参照
    4.関連知識
  • MDN-bind、call、apply紹介