JS-caller,call,call,apply,bind

9618 ワード

原文:(http://www.cnblogs.com/Ghost-Draw-Sign/articles/1530108.html)参考:http://www.cnblogs.com/libin-1/p/5823025.html 上記の概念を言及する前に、まずjavascriptの関数の暗黙的パラメータについて説明したいです。
アルグメンント.
オブジェクトは、実行中の関数とその関数を呼び出すパラメータを表します。
[function.]argments[n]パラメータ:function:オプション。現在実行中のFunctionオブジェクトの名前。n:オプションを選択して、Functionオブジェクトに渡す0からのパラメータ値インデックスです。Agmentsは関数コールを行う場合、指定されたパラメータの他に作成された隠しオブジェクトです。Agmentsは同じ配列であり、配列の対象ではない。配列のようなアクセス特性と方式があるため、対応する単一パラメータの値にアーグメンnts[n]でアクセスでき、配列長属性のlengthを持つという。また、argmentsオブジェクトは、関数宣言によって定義されたパラメータリストに限定されず、実際に関数に伝達されるパラメータを格納し、argmentsオブジェクトを明示的に作成することができません。アーグメンントオブジェクトは関数の開始時のみ使用できます。
function ArgTest(a, b){
    var i, s = "The ArgTest function expected ";
    var numargs = arguments.length;      //           。
    var expargs = ArgTest.length;        //        。
    if (expargs < 2)
       s += expargs + " argument. ";
    else
       s += expargs + " arguments. ";
    if (numargs < 2)
       s += numargs + " was passed.";
    else
       s += numargs + " were passed.";
    s += "

" for (i =0 ; i < numargs; i++){ // 。 s += " Arg " + i + " = " + arguments[i] + "
"; } return(s); // 。 }
ここには、アーグメンントが配列ではないことを示すコードが追加されています。
 Array.prototype.selfvalue = 1;
 alert(new Array().selfvalue);
 function testAguments(){
      alert(arguments.selfvalue);
 }
実行コードは、最初のalertが1を示しています。これは配列オブジェクトがselfvalue属性を持っていることを表しています。値は1です。関数testAggamentsを呼び出したときに、「undefined」と表示されていることが分かります。アーグメンツの属性ではなく、つまり、アーグメンツは配列オブジェクトではありません。
caler〓〓〓〓〓〓〓〓
関数の参照を返します。関数は現在の関数を呼び出します。
functionName.caller
関数の参照を返します。関数は現在の関数を呼び出します。
説明は関数に対して、caler属性は関数実行時のみ定義されます。関数がトップレベルで呼び出された場合、calerに含まれるのはnullです。文字列コンテキストでcaler属性を使用すると、結果はfunctionName.toStringと同じで、つまり関数の逆コンパイルテキストが表示されます。
// caller demo {
function callerDemo() {
     if (callerDemo.caller) {
         var a= callerDemo.caller.toString();
         alert(a);
     } else {
         alert("this is a top function");
     }
}
function handleCaller() {
     callerDemo();              //"function handleCaller() { callerDemo();}"
}
calee
実行中のFunctionオブジェクト、すなわち指定されたFunctionオブジェクトの本文を返します。ES 5のヒント:厳格なモードで、argments.calleeはType Errを間違えて報告します。それはもうキャンセルされました。
[function.]argments.caleeは、現在実行中のFunctionオブジェクトの名前であると説明します。calee属性の初期値は、実行中のFunctionオブジェクトです。functionパラメータは、現在実行中のFunctionオブジェクトの名前です。
calee属性は、関数オブジェクト自体への参照を表しているアーグメンントオブジェクトのメンバーであり、匿名関数の再帰性または保証関数のパッケージ化に有利である。
例えば、以下の例の再帰的に1〜nの自然数の和を計算する。この属性は、相関関数が実行中の場合にのみ使用できます。なお、length属性を持つcaleeは、この属性を検証するために使う場合があります。argments.lengthは、実参照長であり、argments.callee.lengthは、変形長であり、これにより、呼び出し時の変形長が実参照長と一致するかどうかを判断することができる。
//callee       
function calleeDemo() {
   alert(arguments.callee);
}  
calleeDemo();    //    function calleeDemo() { alert(arguments.callee);}
//      
function calleeLengthDemo(arg1, arg2) {
     if (arguments.length==arguments.callee.length) {
         window.alert("           !");
         return;
     } else {
         alert("    :" +arguments.length);
         alert("    : " +arguments.callee.length);
     }
}
//    
var sum = function(n){
   if (n <= 0)                        
   return 1;
   else
     return n +arguments.callee(n - 1)
}
一般的な再帰関数を比較:
var sum = function(n){
     if (1==n) return 1;
else return n + sum (n-1);
呼び出し時:alert(sum(100));は、関数の内部にsum自身への参照が含まれています。関数名は変数名だけで、関数内部でsumを呼び出します。すなわちグローバル変数を呼び出すのに相当します。呼び出し自体をうまく表現できない場合、caleeを使うのが良い方法です。
apply and call噫
それらの役割は、関数を別のオブジェクトに結びつけて実行することであり、両者はパラメータ方式を定義するだけで違いがあります。call(thisArg[,arg 1,arg 2…])
   **     this         thisArg**,    **                **   
applyの説明書〓〓〓〓〓〓〓
   argArray               arguments   ,        `TypeError`。
最初のパラメータは必須です。null、undefined、thisはできますが、空ではありません。null、undefined、thisはグローバルオブジェクトを指定するのと同じです。
コールの説明
  call                           thisArg      。      **       thisArg  ,   Global       thisArg**
関連技術:callとappyのもう一つのテクニックは中にあります。callとappyを使って別の関数(クラス)を適用した後、現在の関数(クラス)はもう一つの関数(クラス)の方法または属性を備えています。これは継承とも言えます。以下の例を参照してください
//      
   function base() {
        this.member = " dnnsun_Member";
        this.method = function() {
            window.alert(this.member);
        }
   }
   function extend() {
        base.call(this);
        window.alert(member);
        window.alert(this.method);
   }
上記の例では、コールした後、extedはベースの方法と属性を継承することができます。多重継承:
function Class10()  
{  
    this.showSub = function(a,b)  
    {  
        alert(a-b);  
    }  
}  
function Class11()  
{  
    this.showAdd = function(a,b)  
    {  
        alert(a+b);  
    }  
}  
function Class2()  
{  
    Class10.call(this);  
    Class11.call(this);  
}
二つのcallを使って多重継承を実現するのはもちろん、jsの継承には他の方法があります。例えば、プロトタイプチェーンを使用します。
アプリ方法には以下のような応用があります。
  • は、行列の最大数
  • を見つける。
    1     var a = [2, 4, 5, 7, 8, 10];
    2 
    3     console.log(Math.max.apply(null, a)); //10
    4     console.log(Math.max.call(null,2, 4, 5, 7, 8, 10)); //10
    
    Javascriptでは、行列の中の最大値を見つける方法が提供されていません。Function.prototypeから継承されたappyとMath.max方法を組み合わせると、配列の最大値が返されます。
  • は、配列の空要素をundefinedに変更し、Arayコンストラクタを利用して配列の空要素をundefinedに変化させる。
  •  console.log(Array.apply(null, [1, , 3])); // [1, undefined, 3]
    
    空の要素とundefinedの違いは、配列のforEach方法が空の要素をスキップしますが、undefinedとnullはスキップされません。したがって、内部要素を遍歴すると、異なる結果が得られます。
    var a = [1, , 3];
        a.forEach(function(index) {
            console.log(index); //1,3 ,      。
        })
    
        Array.apply(null,a).forEach(function(index){
            console.log(index);    ////1,undefined,3  ,       undefined
        })
    
  • は、同様の配列のオブジェクトを変換し、また、配列オブジェクトのslice方法を利用して、同じ配列のオブジェクト(例えば、argmentsオブジェクト)を本物の配列に変換することができる。もちろん、slice法の重要な応用は、類似の配列の対象を本物の配列に変えることである。このアプリケーションは、callとappyの両方で実現できます。
  • 1     console.log(Array.prototype.slice.apply({0:1,length:1}));    //[1]
    2     console.log(Array.prototype.slice.call({0:1,length:1}));    //[1]
    3     console.log(Array.prototype.slice.apply({0:1,length:2}));    //[1,undefined]
    4     console.log(Array.prototype.slice.call({0:1,length:2}));    //[1,undefined]
    
    1     function keith(a,b,c){
    2         return arguments;  //     
    3     }
    4 
    5     console.log(Array.prototype.slice.call(keith(2,3,4)));    //[2,3,4]  
    
    上記のコードのcallは、appyメソッドのパラメータが対象ですが、返ってきた結果は配列で、オブジェクトを配列に変換する目的があります。上記のコードから、この方法が作用する前提は、処理されるオブジェクトにはlength属性と対応する数字キーが必要であることがわかる。
    ちなみに、javascriptフレームワークprototypeでは、appyを使って定義タイプのパターンを作成します。
    実は現代のサイズは以下の通りです。
    var Class = {
            create: function() {
                return function() {
                     this.initialize.apply(this, arguments);
                }
           }
       }
    
    解析:コードから見ると、オブジェクトは一つの方法しか含まれていません。Createは関数、すなわちクラスを返します。しかし、これはまた、クラスのコンストラクタであり、initializeを呼び出すが、この方法はクラス作成時に定義される初期化関数である。このようなルートで、プロジェクトのクラス作成モードを実現できます。
    例:
    var vehicle=Class.create();
      vehicle.prototype={
         initialize:function(type){
             this.type=type;
         }
         showSelf:function(){
             alert("this vehicle is "+ this.type);
         }
      }
    
       var moto=new vehicle("Moto");
       moto.showSelf();
    
    bind菗.
    bindメソッドは、関数内部のthis指向(実行時の作用領域)を指定し、新しい関数を返します。bind法はすぐに関数を実行するわけではない。
    var keith = {
            a: 1,
            count: function() {
                console.log(this.a++);
            }
        };
    
        var f = keith.count;
        f(); //NaN
    
    上のコードでは、countメソッドをf変数に割り当てると、thisオブジェクトは、もはやkeithオブジェクトではなく、windowオブジェクトを指します。window.aはデフォルトでundefinedです。インクリメント演算を行ったらundefined++はNaNになります。この問題を解決するためには、ビッド法を使用して、keithオブジェクト内のthisをkeithオブジェクトに結合したり、直接呼び出したりすることができます。
    1     var f = keith.count.bind(keith);
    2     f(); //1
    3     f(); //2
    4     f(); //3
    
    もちろん、thisは他のオブジェクトにも結合することができます。
    var obj = {
            a: 100
        };
        var f = keith.count.bind(obj);
        f(); //100
        f(); //101
        f(); //102
    
    function keith(a, b) {
            return a + b;
        }
        console.log(keith.apply(null,[1,4])); //5
        console.log(keith.call(null,1,4)); //5
        console.log(keith.bind(null, 1, 4)); //keith()
        console.log(keith.bind(null, 1, 4)()); //5
    
    bind方法は、appy/call方法と非常に似ています。もう少しパッケージ化しても、上記DEMOを事例としています。
    function Point(x, y){
        this.x = x;
        this.y = y;
    }
    Point.prototype.move = function(x, y) {
        this.x += x;
        this.y += y;
    }
    var p = new Point(0,0);
    var circle = {x:1, y:1, r:1};
    // p.move.call(circle, 2, 1);
    // p.move.apply(circle, [2, 1]);
    var circleMove = p.move.bind(circle, 2, 1);    //      move  
    circleMove();    //     
    
    上記のDEMOからわかるように、bindメソッドは、実際には、appy/callの方法を緩めることであり、パッケージ化されたとも言え、後続の起動に便利であり、実質的には次のコードに相当する。
    function circleMove() {
        p.move.call(circle, 2, 1);
    }
    circleMove();
    
    bind方法の互換性は菗璖33751;に適応する。
    EcmaScript 5にbindという方法を拡張しました。使い方は以下の通りです。
    if (!Function.prototype.bind) {
        Function.prototype.bind = function(obj) {
            var      _self = this
                      ,args = arguments;
            return function() {
                _self.apply(obj, Array.prototype.slice.call(args, 1));
            }
        }
    }
    
    call、appy、bindの3つの方法は実は全部Funtions.prototypeから継承されています。
    call,appy,bindの3つの違いが分かります。①bindの戻り値は関数です。②コールとappyメソッドは、呼び出し直後に実行されます。ビッドが呼び出された後は元の関数に戻ります。もう一回呼び出さなければならないです。ちょっと閉じたような味がします。