JavaScript深入なbindのシミュレーション実現

5092 ワード

JavaScript深入なbindのシミュレーション実現
bind()メソッドは、関数を作成します.この新しい関数が呼び出されると、bind()の最初のパラメータは彼の運転時のthisとなります.その後のシーケンスパラメータは、伝達パラメータの前に彼のパラメータとして使用されます.
実はこれもビッド関数の二つの特徴が見られます.
  • は、新しい関数
  • を返します.
  • は、パラメータ
  • に入ることができる.
    まず例を見ます.戻り関数の実現です.
    var foo = {
        value : 2
    }
    function bar(){
        console.log(this.value)
    }
    var bindBar = bar.bind(foo)
    bindBar();
    // 2
    
    
    私たちが定義したbind 2の関数はFuntions.prototypeで定義します.私たちの関数はすべてFunctionによって作成されました.windowもFunction.prototypeの例です.
    window.__proto__.__proto__.__proto__.__proto__.__proto__ == Object.prototype.__proto__
    //true
    Object.__proto__  == Function.prototype
    //true
    
    window.__proto__.__proto__.__proto__.__proto__ == Function.prototype.__proto__
    //true
    
    Function.prototype.bind2 = function(context){
        console.log(111, this)
        //  this      。   bar  ,   bar  
        //context              
        //bar    this  context  this
        
        var self = this
        return function(){
        self.apply(context)
        }
    }
    
    var foo = {
        value:3
    }
    function bar(){
        console.log(this.value)
    }
    var bindFoo = bar.bind2(foo);
    //bind2      bind2     ,         
    console.log(bindFoo()) //3
    
    参考文献のシミュレーション実現
    まず騒ぎ立てるビッドを見て、先ほどの騒例を続けます.
    var foos = {
        value:4
    }
    function foo(name,age){
        console.log(name)
        console.log(age)
        console.log(this.value)
    }
    
    var bindFoo = foo.bind(foos, 'xiaolizi')
    bindFoo(18)
    // xiaolizi
    //18
    // 4
    
    fooはnameとageの二つのパラメータが必要です.そして、bindの時に、一つのnameだけを伝えて、戻ってきた関数を実行する時に、もう一つのパラメータageを伝えます.ですから、もう二番目のバージョンはアーグメンントを処理する必要があります.
       
    Function.prototype.bind2 = function(context){
        var self = this;
    //  arguments      ,    slice,    call  this   
    // slice(1)              ,             context。
        var args = Array.prototype.slice.call(arguments,1)
        return function(){
        //             ,         
            var bindArgs = Array.prototype.slice.call(arguments)
            return self.apply(context,args.concat(bindArgs))
            }
    }
    
    var foos = {
        value:4
    }
    function foo(name,age){
        console.log(name)
        console.log(age)
        console.log(this.value)
    }
    
    var bindFoo = foo.bind2(foos, 'xiaolizi')
    bindFoo(18)
    
    コンストラクタ効果のシミュレーション実現
    一つのバインディング関数もnewオペレータを使用してオブジェクトを作成することができます.このような挙動は、関数をビルダとして扱い、提供されたthis値を無視しながら、呼び出し時のパラメータをアナログ関数に提供します.
    つまり、bindが返した関数がコンストラクタとしている場合、bindで指定されたthis値は無効になりますが、入ってきたパラメータは依然として有効です.
    var value = 2;
    
    var foo = {
        value: 1
    };
    
    function bar(name, age) {
        this.habit = 'shopping';
        console.log(this.value);
        console.log(name);
        console.log(age);
    }
    
    bar.prototype.friend = 'kevin';
    
    var bindFoo = bar.bind(foo, 'daisy');
    
    var obj = new bindFoo('18');
    // undefined
    // daisy
    // 18
    console.log(obj.habit);
    console.log(obj.friend);
    // shopping
    // kevin
    
    注意:グローバルとfooでvalue値を宣言しましたが、最後にundefindに戻りました.紐付けのthisが失効したと説明しています.newのシミュレーションが実現されていることを知ると、この時のthisがObjを指していることが分かります.
    //    
    Function.prototype.bind2 = function (context) {
        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
    
        var fBound = function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            //         ,this     ,      true,       this      ,               
            //       demo   ,     `this instanceof fBound ? null : context`,         ,  null    this ,      habit   
            //         ,this    window,      false,       this    context
            return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
        }
        //         prototype        prototype,                 
    //     fBound   function,                 ,            ,  this   ,         ,this           fBound.prototype      this   
        fBound.prototype = this.prototype;
        return fBound;
    }
    
    コンストラクタ効果の最適化実現
    しかし、この書き方では、fBound.prototype=this.prototypeを直接修正しても、バインディング関数のprototypeを直接修正します.この時、私達は一つの空の関数で中継できます.
    //    
    Function.prototype.bind2 = function (context) {
    
        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
    
        var fNOP = function () {};
    
        var fBound = function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
        }
    
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    }
    
    最終版
    Function.prototype.bind2 = function (context) {
    
        if (typeof this !== "function") {
          throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
        }
    
        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
    
        var fNOP = function () {};
    
        var fBound = function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
        }
    
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    }
    
    以上は文章を参考にして自分の理解を加えました.
    JavaScript深入なbindのシミュレーション実現