在JavaScript編程中,理解this、call和apply是道檻,若是能正確的理解它們的本質及其應用。那麼在之後的JavaScript中會駕輕就熟。編程
跟別的語言截然不同的是,JavaScript的 this 老是指向一個對象,而具體指向哪一個對象是在運行時基於函數的執行環境動態綁定的,而非函數被聲明時的環境。數組
this 的指向瀏覽器
具體到實際應用中,this的指向能夠分爲如下4中。app
當函數做爲對象的方法被調用時,this指向該對象:函數
var obj={
num:1,
getNum:function(){
alert( this === obj); //輸出:true
alert( this.a ); //輸出:1
}
};
obj.getNum();
當函數不做爲對象的屬性被調用時,也就是咱們常說的普通函數調用方式,此時的this老是指向全局對象。在瀏覽器的JavaScript裏,這個全局對象是window對象。this
window.name = 'globalName';
var myObj={
name:'khadron',
getName:function(){
return this.name;
}
};
var getName = myObj.getName;
console.log( getName() ); //輸出:globalName
在ECMAScript5的strict模式下,這種狀況下的this已經被規定爲不會指向全局對象,而是undefined:spa
function func(){
'use strict'
alert( this ); //輸出:undefined
};
func();
JavaScript中沒有類,可是能夠從構造器中建立對象,同時也提供了new運算符,使得構造器看起來更像是一個類。prototype
出了宿主提供的一些內置函數,大部分JavaScript函數均可以看成構造器使用。構造器的外表跟普通函數如出一轍,它們的區別在於被調用的方式。當用new運算符調用函數時,該函數總返回一個對象,一般狀況下,構造器裏的this就是指向返回的這個對象:對象
var MyClass=function(){
this.name = 'khadron';
};
var obj = MyClass();
alert( obj.name ); //輸出:khadron
但用new調用構造器=時,還須要注意有一個問題,若是構造函數顯示地返回一個object類型的對象,那麼這次運算結果最終會返回這個對象,而不是咱們以前期待的this:ip
var MyClass = function(){
this.name = 'khadron';
return { //顯示地返回一個對象
name: 'q'
};
};
var obj=new MyClass();
alert( obj.name ); //輸出: q
若是構造器不顯示地返回任何數據,或者返回一個非對象類型的數據,就不會形成上述問題:
var MyClass = function(){
this.name='khadron';
return 'q'; //返回string類型
};
var obj = new MyClass();
alert( obj.name ); //輸出:khadron
跟普通函數調用相比,用Function.prototype.call或Function.prototype.apply能夠動態地改變傳入函數的this:
var obj1={
name: 'khadron',
getName:function(){
return this.name;
}
};
var obj2={
name:'q'
};
console.log( obj1.getName() ); //輸出:khadron
console.log( obje1.getName.call( obj2 )); //輸出:q
丟失 this 問題
咱們先看下面的代碼:
var obj={
name:'khadron',
getName:function(){
return this.name;
}
}
console.log(obj.getName()); //輸出:khadron
var getNameFunc=obj.getName();
console.log(getNameFunc()); //輸出:undefined
當調用obj.getName時,getName方法是做爲obj對象的屬性被調用的,此時的this指向obj對象,因此obj.getName()輸出’khadron’。
當另一個變量getNameFunc來引用obj.getName,而且調用getNameFunc時,此時是普通函數調用方式,this指向是全局變量window,因此程序執行的結果是undefined。
ECAMScript3給Function的原型定義了兩個方法,他們是Function.prototype.call和Function。prototype.apply。在實際開發中,特別是在一些函數式風格的代碼編寫中,call 和 apply方法尤其有用。
call 和 apply 的區別
call 和 apply 的做用如出一轍,區別僅在於傳入參數形式的不一樣。
apply接受兩個參數,第一個參數制定了函數體內this對象的指向,第二個參數爲一個帶下標的集合,這個集合能夠爲數組,也能夠爲類數組,apply方法把這個集合中的元素做爲參數傳遞給被調用的函數:
var func = function(a,b,c){
alert( [a,b,c] ); // 輸出:[1,2,3]
};
func.apply( null,[]1,2,3 );
在上段代碼中,參數1,2,3被放在數組中一塊兒傳入func函數,它們分別對相應func參數列表中的a、b、c。
call傳入的參數數量不固定,跟apply相同的是,第一個參數也是表明函數體內的this指向,從第二個參數開始日後,每一個參數被一次傳入函數:
var func = function(a,b,c){
alert( [a,b,c] ); //輸出:[1,2,3]
};
func.call( null,1,2,3 );
當調用一個函數時,JavaScript的解釋器並不會計較形參和實參的數量、類型以及順序上的區別,JavaScript的參數在內部就是一個數組來表示的。
call是包裝在apply上面的一課語法糖,若是咱們明確地知道函數接受多少個參數,並且想一目瞭然地表達形參和實參的對應關係,那麼也能夠用call來傳遞參數。
當調用call或者apply的時候,若是咱們傳入的第一個參數爲null,那麼函數體內的this會指向默認的宿主對象,在瀏覽器中則是window:
var func = function(a,b,c){
alert( this === window ); //輸出true
};
func.apply( null,[1,2,3] );
但若是是在嚴格模式下,函數體內的this指向爲null
var func = function(a,b,c){
'use strict'
alert( this === null ); //輸出true
}
有時候咱們使用call 或者 apply 的目的不在於指定this指向,而是另有用途,好比借用其餘對象的方法,咱們能夠傳入null來代替某個具體的對象:
Math.max.apply( null, [1,2,4,5,3]); //輸出5