JS中的this、apply、call、bind(經典面試題)


 

1、什么是this

在JavaScript中this可以是全局對象、當前對象或者任意對象,這完全取決於函數的調用方式,this 綁定的對象即函數執行的上下文環境(context)。

為了幫助理解,讓我們來一起看一段代碼:

復制代碼
// 作為對象方法調用
var test = {
    a : 5,
    b : 6,
    sum : function () {
        return this.a + this.b;     // 此處this = test
    }
}
alert(test.sum());     // 11
復制代碼

作為對象調用時this很容易理解,this等價於sum的調用者即上訴的test對象,如果作為函數調用時this=?

復制代碼
// 作為函數調用
a = 4;
b = 3;
function sum(){
    return this.a + this.b;         // 此處this = window
}
alert(sum());      // 7
復制代碼

此時函數sum是做為window對象的一個全局函數,因此sum的調用者為window,即this = window。

復制代碼
var test = {
    a : 5,
    b : 6,
    sum : function (a,b) {
        function getA(a) {
            this.a = a;         // 在window上增加了一個全局變量a
            return this.a;     // 此處this = window
        }
        function getB(b){
            this.b = b;         //在window上增加了一個全局變量b
            return this.b;     // 此處this = window
        }
        return getA(a) + getB(b);
    }
}
alert(test.sum(4,3));  // 7
alert(a);              // 4     
alert(b);              // 3
復制代碼

 在這種情況下,我們希望getA() 和getB() 返回的值是test.a和test.b,但是此時閉包函數(即函數中的函數)getA和getB中this並不指向test的實例,該怎么辦呢?我們不妨試試下面的方法:

復制代碼
var test = {
    a : 5,
    b : 6,
    sum : function () {
        var self = this;    // 此處this = test的實例
        function getA() {
            return self.a;
        }
        function getB(){
            return self.b;
        }
        return getA() + getB();
    }
}
alert(test.sum());
alert(a);     // 此處報錯:a is not defined
alert(b);    // 此處報錯:a is not defined
復制代碼

在test對象的sum函數中用一個局部變量self來保存當前的this指針,這樣在閉包函數getA和getB中就能通過self變量獲取test實例的屬性了。

看起來這樣就能夠解決閉包函數中this的問題了,但是,如果調用sum函數的並不是test的實例呢,這個時候var self=this還能起到作用,獲取到test的實例嗎?

 2、使用call、apply和bind改變函數執行時的上下文(this)

使用call、apply和bind都能夠是函數的上下文發生改變,那我們來具體看看這記者之間的區別吧。

call方法:

語法:call([thisObj[,arg1[, arg2[,   [,.argN]]]]])

定義:調用一個對象的一個方法,以另一個對象替換當前對象。

說明:call 方法可以用來代替另一個對象調用一個方法。call 方法可將一個函數的對象上下文從初始的上下文改變為由 thisObj 指定的新對象。

     如果沒有提供 thisObj 參數,那么 Global 對象被用作 thisObj。

apply方法:

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

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

說明:如果 argArray 不是一個有效的數組或者不是 arguments 對象,那么將導致一個 TypeError。

        如果沒有提供 argArray 和 thisObj 任何一個參數,那么 Global 對象將被用作 thisObj, 並且無法被傳遞任何參數。

bind方法:

語法:bind(thisArg[, arg1[, arg2[, ...]]])

定義:將接受多個參數的函數變換成接受一個單一參數。

說明:bind()方法所返回的函數的length(形參數量)等於原函數的形參數量減去傳入bind()方法中的實參數量(第一個參數以后的所有參數),因為傳入bind中的實參都會綁定到原函數的形參。

哎呀媽呀,講了那么多理論的東西,我都暈了,還是看看實際的例子:

復制代碼
var test = {
    a : 5,
    b : 6,
    sum : function (a,b) {
        var self = this;
        function getA() {
            return self.a;
        }
        function getB(){
            return self.b;
        }
        alert(a);
        alert(b);
        return getA() + getB();
    }
}
var obj = {a:2,b:3};
alert(test.sum.call(obj,4,5));      // 調用時self = this = obj,alert順序4,5,5
alert(test.sum.apply(obj,[6,7]));   // 調用時self = this = obj,alert順序6,7,5
var sum = test.sum.bind(obj,8);     // 此處返回一個只有一個參數的函數sum(b)
alert(sum(9));                      // 調用時self = this = obj,alert順序8,9,5
復制代碼

從上面的例子我們可以很清晰的看到call、apply和bind之間的區別。其中call和apply是差不多的,只是傳參的形勢不同(apply的第二個參數為一個數組或arguments),他們都是直接直接執行函數;

而bind函數將test.sum簡化為另一個全局函數sum(b),sum(b)只需要傳入一個參數即可。

3、解決js中煩人的this

call、apply和bind都可以應用於繼承,在這里不再過多贅述,網上有很多這樣的例子,參考:http://blog.csdn.net/wyyfwm/article/details/46349071

而我想講一下這段時間我遇到的一些關於this比較頭疼的事情。

復制代碼
<button id="btn">煩人的this</button>
<script>
    var test = {
        isSum: true,
        sum: function (event, a, b) {
            if (this.isSum) {   // this = button,這個時候不會執行alert(a+b)
                alert(a + b);
            }
        }
    }
    var button = document.getElementById("btn");
    button.addEventListener("click", test.sum, false);
</script>
復制代碼

這里我們就能發現問題所在了,當ID為btn的按鈕被點擊時會觸發test.sum函數,但是這個時候的this=button,而且參數a、b如何傳入呢?

這里就能夠使用bind函數了,將test.sum函數簡化為另一個新的函數,同時傳入參數a和b,我們再看看下面的代碼:

復制代碼
<button id="btn">this</button>
<script>
    var test = {
        isSum: true,
        sum: function (a, b,event) {
            if (this.isSum) {  // 此處this=test,this.isSum = true
                alert(a + b);  // 9
            }
        }
    }
    var button = document.getElementById("btn");
    button.addEventListener("click", test.sum.bind(test,4,5), false);  // 此處test.sum.bind(test,4,5)返回一個新的函數function(event),
</script>
復制代碼

從上面的代碼我們可以看到test.sum.bind(test,4,5)返回一個新的函數function(event),test、4、5分別被綁定到test.sum的上下文、參數a、參數b中。
當ID為btn的按鈕被點擊時會觸發test.sum函數,此時改函數中的this=test,a=4,b=5。

這樣就可以解決事件綁定時的this以及傳參的問題了,包括現在常用js框架中的事件綁定,如jQuery、signals.min.js等等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM