Javascriptのbindを操る

5977 ワード

前言
今日は暇なので、MDNをぶらつきました.ふと一つの方法を見ました.この方法を使う以外に、この方法をよく考えたことがないです.そこで、キルティングのことを見つけました.ブログを書きます.
基礎知識の概要
資料を見てみても、実は簡単ではないことが分かります.理解するには多くの基礎知識が必要です.ここにいくつか並べても、まとめと復習になります.
関数
以下の言葉は「JavaScript言語精粋」から来ています.
関数を呼び出したら、現在の関数の実行を一時停止します.新しい関数には、コントロールとパラメータを伝達します.宣言時に定義された形式パラメータに加えて、各関数は2つの追加パラメータを受信します.パラメータthisはオブジェクト指向プログラミングにおいて非常に重要であり、彼の値は呼び出しモードに依存する.JavaScriptの中には全部で4つの呼び出しモードがあります.方法コールモード、関数コールモード、コンストラクションモードとappyコールモード.これらのモードは,鍵パラメータthisをどのように初期化するかにおいて差がある.
メソッドコールモード
関数がオブジェクトの属性として保存されている場合、それを方法と呼びます.一つの方法が起動されると、thisはオブジェクトに結合されます.
var info = {
    name: 'yuanzm',
    sayName: function() {
        console.log(this.name);
    }
}
info.sayName();    //yuanzm
関数コールモード
一つの関数がオブジェクトの属性ではない場合、彼は関数として呼び出されます.このモードで関数を呼び出すと、thisはグローバルオブジェクトにバインドされます.これは言語設計上の誤りです.言語のデザインが正しいなら、thisは外部関数に結合されたthis変数であるべきです.
var name = "yuanzm"
var sayName = function() {
    console.log(this.name);
}
sayName();// yuanzm
コンストラクタ呼び出しモード
関数の前にnewを持って呼び出したら、その関数に接続されたプロトタイプのメンバーの新しいオブジェクトが陰で作成されます.(JavaScriptプロトタイプに関する知識はここでは説明しない)
function Info(name) {
    this.name = name;
}
Info.prototype.sayName = function() {
    console.log(this.name);
}
var info = new Info('yuanzm');
info.sayName();//yuanzm
アプリ呼び出しモード
MDNの定義によると
The appy()method cars a function with a given this value and argmens provided as an array(or an array-like oject)
var numbers = [5, 6, 2, 3, 7];
var max = Math.max.apply(null, numbers);
クラス配列
クラスの配列は、次のように定義されています.
  • は、オブジェクト要素を指すデジタルインデックスの下付きおよびlength属性がオブジェクトに教える要素の個数
  • を有する.
  • は、push、forEach、indexOfなどの配列オブジェクトが有する方法
  • を有しない.
    上記の定義に適合するクラス配列は、次のように長いです.
    var my_object = {
        '0': 'zero',
        '1': 'one',
        '2': 'two',
        '3': 'three',
        '4': 'four',
        length: 5
    };
    前述のFunction.prototype.bind()もクラス配列である.多くの場合、クラスの配列を処理するのに一番手間のかからない方法は配列に変換することです.非常に興味深い話題になります.クラス配列を行列に変換します.クラスの配列を配列に変換するのは非常に簡単で、Aray自身の方法を呼び出してもいいです.
    Array.prototype.slice.call(arguments);
    その中でcallをappyに変えても同じです.簡単に説明しますと、sliceメソッドの従来の呼び出し方式はargumentsで、arrayの一節にarray.slice(start, end)をして、まずarray[start]をコピーして、array[end]までコピーします.前に述べたように、apply(またはcall)は関数呼び出しの文脈を切り替えることができます.つまり は手動で操作が必要なargmentsを結合しました.argmentsは配列と同じ下付きを持っていますので、この方法も実行可能です.この配列は通常の配列のすべての方法を持っています.同時に、argmentsそれぞれの下付き文字に対応する値を持っています.
    ビッド
    概要
    これだけ言って、ようやく主役が登場しました.しかし、その役割を明らかにするためには、やはり必要なコードを捨てなければなりません.
    var name = 'yuan'
    var info = {
        name: "yuanzm",
        sayName: function() {
            console.log(this.name);
        }
    }
    var sayName = info.sayName;
    //          yuanzm ,      yuan    。          ,           。
    //      info.sayName()      ,           ,     this info  ;
    //        sayName info.sayName,         ,            ,     yuan。
    sayName();    // yuan 
    私たちはビッドを使って最後に得たい結果がArray.prototype.slice.call(arguments);です.
    今は私たちはビッドをちゃんと紹介できます.MDNの定義によると:
    The bind()method creates a new function that,when caled,has its this keyword set to the provided value,with a given sequence of argmens preceding any provided when the new function caled.
    このバインディング関数を呼び出すと、バインディング関数が作成されたときに、最初のパラメータがthisとして入ってきます.bind()メソッドの2番目と以降のパラメータが結合関数として動作します.自身のパラメータは、順番に元の関数のパラメータとして呼び出されます.彼の文法は:
    fun.bind(thisArg[, arg1[, arg2[, ...]]]);
    問題を解決する
    ビッドがあれば、上記の問題は私達が望む結果を得ることができます.
    var name = 'yuan'
    var info = {
        name: "yuanzm",
        sayName: function() {
            console.log(this.name);
        }
    }
    var sayName = info.sayName.bind(info);
    sayName();    // yuanzm
    bindの使い方はMDNの上で詳しく説明されていますが、ここではMDNをそのまま運ぶ目的ではないので、ここでbindに関する部分はここに来て、次にJavascriptライブラリのbindの実現を見に行きます.
    Prottypeの中のbind
    昔のProttypeのbindの書き方は以下の通りです.
    Function.prototype.bind = function(){ 
        // bind  Function prototype  ,        bind  ,   this          (          )
        var fn = this;
        //     ,                
        var args = Array.prototype.slice.call(arguments);
        //    shift             , bind       ,           this   
        var object = args.shift();
        return function(){ 
            return fn.apply(object,
            //    args                  ,              。
            // array.concat(items...)         ,     array    ,          item     。
            //     item     ,            。
            //                 ,            (    )         。
            args.concat(Array.prototype.slice.call(arguments))); 
        }; 
    };
    最新バージョン1.7.2(205.6.23公式サイトを参照)においてhttps://github.com/sstephenson/prototype/blob/master/src/prototype/lang/function.js#L115ビndは以下のようなものです.
    function bind(context) {
        if (arguments.length < 2 && Object.isUndefined(arguments[0]))
            return this;
    
        if (!Object.isFunction(this))
            throw new TypeError("The object is not callable.");
        
        var nop = function() {};
        var __method = this, args = slice.call(arguments, 1);
    
        var bound = function() {
            var a = merge(args, arguments);
            // Ignore the supplied context when the bound function is called with
            // the "new" keyword.
            var c = this instanceof bound ? this : context;
            return __method.apply(c, a);
        };
        
        nop.prototype   = this.prototype;
        bound.prototype = new nop();
    
        return bound;
    }
    
    いくつかの異常な状況判断を加えた以外、コアコードは以前と大差がないことが分かります.
    おわりに
    いずれにしても、apply、call、bindはマニュアルでthisオブジェクトを結びつけるためであり、目的には違いがない.しかし、bindと他の2つの明らかな違いは、bindが新しい関数を生み出すことであり、この関数は予め設定されたパラメータを持つことができます.これは多くの場合、appyやcallより使いやすいです.appy、call、bindを合理的に利用すれば、javaScriptコードがより優雅になります.
    参考資料
    Function.prototype.apply()JavaScriptの癖8:「クラス配列対象」how does Aray.prototype.slip.call work?JavaScript’s Apple,Call,and Bind Methods are Essential for JavaScript Professionals