call()、appy()、bind()を手動で実現します.

3712 ワード

この文章は簡単にcall()、appy()、bind()を実現する構想を紹介しています.
callを実現します.
ターゲット関数のthisは、導入された最初のオブジェクトに向けられ、パラメータは一定の長さであり、直ちに実行されます.
構想を実現する
  • は、オブジェクトの属性
  • として、オブジェクト関数を変更することができます.
  • は、argmentsクラスの配列オブジェクトを利用してパラメータ不定長
  • を実現する.
  • はオブジェクトの属性を増やすことができませんので、最後にdelete
  • が必要です.
    不安定なパラメータを関数にどうやって伝えますか?3つの方法があります.eval、apply、ES 6の構文です.
    eval('obj.fn('+args+'));
    obj.fn.apply(obj,args);
    obj.fn(...args);
    Function.prototype.mycall = function(obj){
             var args = Array.prototype.slice.apply(arguments,[1]);
             obj.fn = this;
             obj.fn(...args);//es6     ,     obj.fn.apply(obj,args);
             delete  obj.fn;
    }
    evalを使用すると、evalは一つの文字列を変数として解析しますので、もし入ってきたパラメータが文字列なら、xxx is not definedを報告します.解決方法は以下の通りです.
    Function.prototype.mycall = function(obj){
            obj = obj||window;
             var args = [];
            for(var i = 1 ; i < arguments.length; i++) {
                      args.push('arguments[' + i + ']');
             }
    
             obj.fn = this;
             eval('obj.fn('+args+'));
             delete  obj.fn;
    }
     
    appyを実現する()
    call()との違いは一つしかないです.appyの2番目のパラメータは配列です.
    Function.prototype.myapply = function(obj,arr){
            obj.fn = this;
             if(!arr){
                 obj.fn();
            }else{
                 var args = []; 
           for(var i = 0; i < arr.length; i++) {
                 args.push('arr[' + i + ']');
             }
             
             eval('obj.fn('+args+')');
     }
           
             delete  obj.fn;
    
    }
    bindを実現する()
    調整された関数と同じ関数を持つ新しい関数を返します.また、この新しい関数はnew演算子を使用することもできます.
    構想を実現する
  • は、新しい関数を返します.
  • bit()着信パラメータの長さは一定ではなく、関数内蔵のアーグメンツオブジェクト配列を使用して、アラy.prototype.slice.call(argments,1)を利用して配列
  • に変換することができます.
  • によって返された新しい関数では、appyを使用して、呼び出された関数のthis指向を変更し、argmentsから変換された配列をappyの2番目のパラメータ
  • として使用する.
  • 戻る新しい関数もnewオペレータを使用することができますので、新しい関数の内部でnewオペレータが使用されているかどうかを判断する必要があります.使用すると、appyの最初のパラメータを新たに作成されたオブジェクトに設定します.もしない場合は、bind()を呼び出した時に入ってきたオブジェクトに設定します..
  • 注意したいのは、newオペレータが使われているかどうかをどう判断するかです.この問題を解決する前に、newオペレータを使う時に具体的に何をしましたか?以下はnewオペレータの簡単な実現過程です.
    //    new       
    
    function newFunc(constructor){
          //   :       obj 
            var obj = {};
           //   :      constructor       obj   
            obj.__proto__ = constructor.prototype;
          //   :      constructor  this  obj,              
            constructor.apply(obj);
          //   :      
            return obj;
    }
    newオペレータのプロセスは継承に相当し、新しく作成した構造関数の例は構造関数のプロトタイプチェーンにアクセスできる.
    newオペレータの実現過程の第三段階では、構造関数constructorのthisをobjに向け、直ちに構造関数内部の動作を実行すると、関数内部の動作を実行する時にnewが使用されているかどうかを判断しないと、構造関数constructorの中のthisをobjに向けてしまうというプロセスが無効になります.具体的な原因は下記の模倣を見て、bind()のコードを実現してください.
    Function.prototype.testBind = function(object){
    
              var that = this,
                  args = Array.prototype.slice.call(arguments,1),
                  bound = function(){
                        return that.apply(this instanceof fNOP?this:object||window,
                             args.concat.apply(Array.from(arguments)));
              };
    
            //        fNOP, bound           
              var fNOP =  function(){};
              fNOP.prototype= that.prototype;   
              bound.prototype= new fNOP();  
      
              return bound;
    }
    ポイント:中継関数fNOPを作成して、boundに間接的にターゲット関数の原型を継承させます. bound.prototype=that.prototypeは直接的に値を割り当てた後、bound.prototypeとthat.prototypeは同じ内容を指しています.bound.prototypeを変えると、that.prototypeに直接影響を及ぼします.乗り換え関数を使います. bound.prototype=new fNOP()はbound.prototypeの_u uぽろぽろfnoP.prototypeを指し、その後fNOP.prototype= that.prototypeなので、bound.prototypeを変えることはthat.prototypeに影響しません.
    また、上のbind()を実現したコードの中でappyを使用しているところは、元のコードに変えられます.