JS中的call、apply、bind方法

鐵鍋發表於2016-11-16

在JavaScript中,callapplybindFunction物件自帶的三個方法,這三個方法的主要作用是改變函式中的this指向。

callapplybind方法的共同點和區別:
applycallbind 三者都是用來改變函式的this物件的指向的;
applycallbind 三者第一個引數都是this要指向的物件,也就是想指定的上下文(函式的每次呼叫都會擁有一個特殊值——本次呼叫的上下文(context)——這就是this關鍵字的值。);
applycallbind 三者都可以利用後續引數傳參;
bind 是返回對應函式,便於稍後呼叫;applycall 則是立即呼叫 。

一、call

call()
語法:

call([thisObj[,arg1[, arg2[, [,.argN]]]]])複製程式碼

定義:呼叫一個物件的一個方法,以另一個物件替換當前物件。

說明: call 方法可以用來代替另一個物件呼叫一個方法。
call 方法可將一個函式的物件上下文從初始的上下文改變為由 thisObj 指定的新物件。

thisObj的取值有以下4種情況:
(1) 不傳,或者傳null,undefined, 函式中的this指向window物件
(2) 傳遞另一個函式的函式名,函式中的this指向這個函式的引用
(3) 傳遞字串、數值或布林型別等基礎型別,函式中的this指向其對應的包裝物件,如 String、Number、Boolean
(4) 傳遞一個物件,函式中的this指向這個物件

function a(){   
  console.log(this);   //輸出函式a中的this物件
}       

function b(){}       

var c={name:"call"};    //定義物件c  

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(c);   //Object複製程式碼

如果你不理解上面的,沒關係,我們再來看一個例子:

function class1(){   
  this.name=function(){   
    console.log("我是class1內的方法");   
  }   
}   
function class2(){ 
  class1.call(this);  //此行程式碼執行後,當前的this指向了class1(也可以說class2繼承了class1)   
}   

var f=new class2();   
f.name();   //呼叫的是class1內的方法,將class1的name方法交給class2使用複製程式碼

常用例子:
(1)

function eat(x,y){   
  console.log(x+y);   
}   
function drink(x,y){   
  console.log(x-y);   
}   
eat.call(drink,3,2);

輸出:5複製程式碼

這個例子中的意思就是用 eat 來替換 drink,eat.call(drink,3,2) == eat(3,2) ,所以執行結果為:console.log(5);
注意:js 中的函式其實是物件,函式名是對 Function 物件的引用。

(2)

function Animal(){   
  this.name="animal";   
  this.showName=function(){   
    console.log(this.name);   
  }   
}   
function Dog(){   
  this.name="dog";   
}   
var animal=new Animal();   
var dog=new Dog();       

animal.showName.call(dog);

輸出:dog複製程式碼

在上面的程式碼中,我們可以看到Dog裡並沒有showName方法,那為什麼(this.name)的值是dog呢?

關鍵就在於最後一段程式碼(animal.showName.call(dog)),意思是把animal的方法放到dog上執行,也可以說,把animal 的showName()方法放到 dog上來執行,所以this.name 應該是 dog。

(3)繼承

function Animal(name){   
  this.name=name;   
  this.showName=function(){   
    console.log(this.name);   
  }   
}   
function Dog(name){   
  Animal.call(this,name);   
}   
var dog=new Dog("Crazy dog");   
dog.showName();

輸出:Crazy dog複製程式碼

Animal.call(this) 的意思就是使用 Animal物件代替this物件,那麼Dog就能直接呼叫Animal的所有屬性和方法。

二、apply()

語法:apply([thisObj[,argArray]])

定義:應用某一物件的一個方法,用另一個物件替換當前物件。

說明:
如果 argArray 不是一個有效的陣列或者不是 arguments 物件,那麼將導致一個 TypeError。
如果沒有提供 argArray 和 thisObj 任何一個引數,那麼 Global 物件將被用作 thisObj, 並且無法被傳遞任何引數。

call 和 apply的區別
對於 apply、call 二者而言,作用完全一樣,只是接受引數的方式不太一樣。

function class1(args1,args2){       
  this.name=function(){      
   console.log(args,args);      
  }     
}     
function class2(){    
  var args1="1";
  var args2="2";
  class1.call(this,args1,args2);  
  /*或*/
  class1.apply(this,[args1,args2]);
}

var c=new class2();   
c.name();

輸出:1 2複製程式碼

call 需要把引數按順序傳遞進去,而 apply 則是把引數放在陣列裡。

既然兩者功能一樣,那該用哪個呢?

在JavaScript 中,某個函式的引數數量是不固定的,因此要說適用條件的話,當你的引數是明確知道數量時用 call ;而不確定的時候用 apply,然後把引數 push 進陣列傳遞進去。當引數數量不確定時,函式內部也可以通過 arguments 這個類陣列物件來遍歷所有的引數。

三、bind
bind是在EcmaScript5中擴充套件的方法(IE6,7,8不支援)
bind() 方法與 apply 和 call 很相似,也是可以改變函式體內 this 的指向。

  MDN的解釋是:bind()方法會建立一個新函式,稱為繫結函式,當呼叫這個繫結函式時,繫結函式會以建立它時傳入 bind()方法的第一個引數作為 this,傳入 bind() 方法的第二個以及以後的引數加上繫結函式執行時本身的引數按照順序作為原函式的引數來呼叫原函式。

注意:bind方法的返回值是函式

var bar=function(){   
  console.log(this.x);   
}
var foo={ 
     x:3   
}   
bar();  
bar.bind(foo)();
 /*或*/
var func=bar.bind(foo);   
func();

輸出:
undefined
3複製程式碼

相關文章