[JavaScript] this、call和apply詳解

    在JavaScript編程中,理解this、call和apply是道檻,若是能正確的理解它們的本質及其應用。那麼在之後的JavaScript中會駕輕就熟。編程

this

    跟別的語言截然不同的是,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調用

跟普通函數調用相比,用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。

call 和 apply

    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
相關文章
相關標籤/搜索