javascriptの中のcall、appy、bindを簡単に話します.

6163 ワード

JavaScriptでは、call、appy、bindはFunctionオブジェクトが持つ3つの方法であり、この3つの方法の主な役割は関数のthisの指向を変えることで、花を接ぎ木`.本論文ではこの3つの方法を詳細に説明し、いくつかの古典的な応用シーンを列挙する. 
call(thisArgs[,args...])
この方法は、運転中の関数の使用者、すなわち関数内のthisオブジェクトを指定したthisArgsパラメータとパラメータリストを渡すことができ、パラメータリストは呼び出し関数に入力されます.thisArgsの評価は以下の4つの場合があります.
(1)伝えない、またはnull、undefinedを伝え、関数の中のthisはwindowオブジェクトを指します.
(2)他の関数の関数名を伝え、関数のthisはこの関数の参照を指します.
(3)伝達文字列、数値またはブールタイプなどの基礎タイプで、関数の中のthisはString、Number、Booleanなどの対応する包装対象を指します.
(4)オブジェクトを伝達し、関数の中のthisはこのオブジェクトを指します.

function a(){
 console.log(this); //    a  this  
}
function b(){} //    b

var obj = {name:'onepixel'}; //    obj

a.call(); //window
a.call(null); //window
a.call(undefined);//window
a.call(1); //Number
a.call(''); //String
a.call(true); //Boolean
a.call(b);// function b(){}
a.call(obj); //Object

これはコールの核心機能です.オブジェクトに定義されていない方法を呼び出すことができます.そしてこの方法はオブジェクトの属性にアクセスできます.このようにすると何かメリットがありますか?後でまた話します.簡単な例を見ます.

var a = {

 name:'onepixel', //  a   

 say:function(){ //  a   
  console.log("Hi,I'm function a!");
 }
};

function b(name){
 console.log("Post params: "+ name);
 console.log("I'm "+ this.name);
 this.say();
}

b.call(a,'test');
>>
Post params: test
I'm onepixel
I'm function a!

b.callを実行すると、文字列`test`がパラメータとして関数bに伝達され、callの役割により、関数bのthisはオブジェクトaを指すので、オブジェクトaの関数bを呼び出すのに相当し、実際にはaにbは定義されていない.
apply(thisArgs[,args[])
applyとcallの唯一の違いは第二のパラメータの伝達方式が違っています.applyの第二のパラメータは配列でなければなりません.callはパラメータリストの伝達を許可します.注意すべきなのは、applyが受信したのはパラメータ配列ですが、関数を呼び出した時にはパラメータリストとして伝達されています.簡単な例を見ます.

function b(x,y,z){
 console.log(x,y,z);
}

b.apply(null,[1,2,3]); // 1 2 3

applyのこの特性はとても重要で、以下の応用シーンでこの特性を言及します.
ビッド(thisArgs[,args...])
bindはES 5によって新たに追加された方法であり、そのパスはコールと似ていますが、コール/applyとは著しい違いがあります.コールコールやapplyは自動的に対応する関数を実行します.bindは対応する関数を実行しません.関数への参照を返します.ざっと見ると、bindはcall/applyより遅れているようですが、ES 5はなぜbindを導入しますか?
実際には、ES 5がbindを導入する本当の目的は、call/applyの不足を補うために、call/applyがターゲット関数に対して自動的に実行されるため、イベントバインディング関数では使用できなくなります.bindは関数thisを変えることを実現すると同時に、目標関数を自動的に実行しないので、上記の問題を完璧に解決できます.例を見れば分かります.

var obj = {name:'onepixel'};

/**
 *  document  click    ,   onClick  
 *   bind    onClick this obj,     p1,p2
 */
document.addEventListener('click',onClick.bind(obj,'p1','p2'),false);

//           
function onClick(a,b){
 console.log(
   this.name, //onepixel
   a, //p1
   b //p2
 )
}

ウェブページをクリックする時、onClickは実行を触発されて、出力onepixel p 1 p 2、onClickの中のthisがbindによってobjオブジェクトに変更されたと説明しています.bindを深く理解するために、私達はbindのpolyfillの実現を見てみます.

if (!Function.prototype.bind) {
 Function.prototype.bind = function (oThis) {
  var aArgs = Array.prototype.slice.call(arguments, 1),
   fToBind = this, //this           
   fBound = function () {
    return fToBind.apply(
     //      var obj = new fBound(),  obj     this,    oThis
     this instanceof fToBind
       ? this //   this  new  obj
       : oThis || this, //     oThis  ,  fBound      this

     //   bind                  ,          
     aArgs.concat(Array.prototype.slice.call(arguments)));
   };

  //                 ,                  
  fBound.prototype = this.prototype;

  //  fBond   ,       
  return fBound;
 };
}

応用シーン一:継承
JavaScriptにはJava、C((zhi)などの高級言語の中のextedキーワードがないので、JSには継承の概念がありません.もし継承したいなら、callとappyはこの機能を実現できます.

function Animal(name,weight){
 this.name = name;
 this.weight = weight;
}

function Cat(){
 Animal.call(this,'cat','50');
 //Animal.apply(this,['cat','50']);

 this.say = function(){
  console.log("I am " + this.name+",my weight is " + this.weight);
 }
}

var cat = new Cat();
cat.say();//I am cat,my weight is 50

new演算子によってcatが生まれた場合、Catのthisはcatの対象を指していますが、継承の鍵はCatでAnimal.call(this,cat','50')を実行したことです.この言葉はcalでthisをサービングパラメータとして伝達しています.そこで、アニマル方法のthisはCatの中のthisを指しています.アニマルではnameとweightの属性が定義されており、catでこれらの属性が定義されていることに相当するため、catオブジェクトはアニマルで定義された属性を持っており、継承の目的を果たしている. 
応用シーンその2:シフォンウッド
以下の内容を話す前に、まずJavaScriptの中の非標準専門用語を紹介します.ARrayLike(クラス配列/擬似配列)
ArayLikeオブジェクトは配列の一部を持っている行為で、DOMではすでに表現されていますが、jQueryの上昇によって、ArayLikeはJavaScriptで大いに異彩を放っています.ArayLikeオブジェクトの精妙はJS原生のArayと似ていますが、自由に構築されています.開発者からJavaScriptオブジェクトに対する拡張から来ています.つまり、プロトタイプはJS原生のArayを汚染することなく自由に定義できます.
ArayLikeオブジェクトは、JSで広く使用されています.例えば、DOMのNodeList、関数のアーグメンツは、配列のように各要素が格納されていますが、配列を操作する方法はありません.配列のいくつかの方法をcallでArayLikeオブジェクトに移動し、その要素を操作する目的を達成することができます.例えば、関数の中のargmentsをこのように遍歴することができます.

function test(){
 //  arguments   Array   
 console.log(
   arguments instanceof Array, //false
   Array.isArray(arguments) //false
 );
 //  arguments   forEach  
 console.log(arguments.forEach); //undefined

 //      forEach   arguments 
 Array.prototype.forEach.call(arguments,function(item){
  console.log(item); // 1 2 3 4
 });

}
test(1,2,3,4);

これ以外にも、applyにとっては、その独自の特性、すなわちapply受信は配列であり、呼び出し関数に伝達するときはパラメータリストで伝達される.この特性は、たとえばcallよりもapplyの方がはるかに優れているように見える.配列の中の最大要素を求めて、行列の中で最大値を得る方法がないことを知っています.一般的には、コードを書くことによって実現する必要があります.一方、Mathオブジェクトには最大値を取得する方法があり、Math.max()という方法があり、max方法はパラメータリストを転送して、これらのパラメータの最大値を返す必要があることを知っています.アプリは、Mathオブジェクトのmax方法を他のオブジェクトに適用するだけでなく、一つの配列をパラメータリストに変換してmaxに渡すこともでき、コードを見れば一目瞭然です.

var arr = [2,3,1,5,4];

Math.max.apply(null,arr); // 5

以上はcallとappyの古典的ないくつかの応用シーンです.これらの技術を熟練に把握して、これらの特性を実際のプロジェクトに応用すると、コードがより味わい深いように見えます.