理解 JS 回調函數中的 this

任何變量或對象都有其賴以生存的上下文。若是簡單地將對象理解爲一段代碼,那麼對象處在不一樣的上下文,這段代碼也會執行出不一樣的結果。瀏覽器

例如,咱們定義一個函數 getUrl 和一個對象 pseudoWindowapp

function getUrl() {
    console.log(this.document.URL);
}

var pseudoWindow = {
    document: {
        URL: "I'm fake URL"
    },

    getUrl1: getUrl,

    getUrl2: function (callback) {
        callback();
        
        this.func = callback;
        this.func();
    }
}

執行 getUrl(),打印出當前頁面的 URL。函數

執行 pseudoWindow.getUrl1(),打印出 I'm fake URLthis

執行 pseudoWindow.getUrl2(getUrl),先打印出當前頁面 URL,後打印 I'm fake URLurl

下面讓咱們用最簡單粗暴的語言來解釋以上代碼。code

概念

什麼是 this?

this 就是函數調用使用的上下文。對象

什麼是上下文

上下文是在句號標記法中,句號前面的那個東西。ip

例如 pseudoWindow.getUrl1pseudoWindowpseudoWindow.getUrl1() 的上下文。作用域

什麼是自由變量

當一個變量沒有綁定到任何上下文時(或者說綁定到頂級做用域時,例如瀏覽器中的 window),它就是自由變量get

什麼是變量對象

變量就是代碼中你所用的標識符,一個標識符就是一個變量,多個變量可能指向同一個對象。例如:

pseudoWindow.getUrl1 === getUrl  // 獲得 true

變量所處的上下文就是對象的做用域。

代碼分解

調用 getUrl()

首先 getUrl 函數是定義在全局環境中,它是一個自由變量,在瀏覽器中(如下描述均爲瀏覽器環境)它的上下文就是 window,因此 window.getUrl()getUrl() 是等價的。所以 this 指向 window 對象,打印出當前 URL。

調用 pseudoWindow.getUrl1()

首先 pseudoWindow 是一個對象,它能夠充當上下文角色。咱們給它定義了一個屬性 getUrl1,你能夠將屬性視爲被綁定到某個上下文的變量,變量 getUrl1 自己又指向了變量 getUrl 所指向的對象,因此 pseudoWindow.getUrl1 === getUrl 纔會爲 true

當咱們調用 pseudoWindow.getUrl1() 時,它的意思是執行 getUrl() 這段代碼,執行代碼所需的參數爲空,上下文爲 pseudoWindow

因此函數中的 this 指向了 pseudoWindow,而 pseudoWindow 對象剛好又有 document 屬性,該屬性剛好又有 URL 屬性,所以打印出 I'm fake URL

調用 pseudoWindow.getUrl2(getUrl)

同理咱們又定義了一個變量 getUrl2,並綁定到 pseudoWindow 對象身上,使之成爲後者的一個屬性。而這個屬性自己又指向一個匿名函數,咱們姑且稱之爲 A,該函數對象接受另外一個函數對象做爲回調函數。

所以執行 pseudoWindow.getUrl2(getUrl) 時,意思是執行代碼 A,執行代碼所需的參數爲 getUrl 這段代碼,上下文爲 pseudoWindow

所以函數 A 中的 this 指向了 pseudoWindow

當程序執行到函數 A 內部的 callback() 時,由於變量 callback 沒有綁定到任何上下文,所以它至關於一個自由變量,它的上下文就指向了 window 對象,所以首先打印出當前頁面的 URL。

接下來 this.func = callback 意味着三件事:

  • 咱們新申明瞭一個變量 func
  • 經過 = 操做符,咱們將該變量指向了 callback 所指向的函數對象。
  • 經過 . 操做符,咱們將該變量綁定到了 this 對象上,使之成爲後者的一個屬性,而本例中 this 指向的就是 pseudoWindow 對象。

因而當程序執行到 this.func() 時,它的意思是執行 callback 這段代碼,執行代碼所需的參數爲空,上下文爲 pseudoWindow。因而打印出了 I'm fake URL

這段代碼帶來的一個反作用是咱們隱式地爲 pseudoWindow 對象添加了一個新的屬性 func,若是咱們想要經過回調的方式打印出 pseudoWindowdocument.URL 屬性,又不想對 pseudoWindow 對象形成任何影響,那麼咱們可使用函數的 apply 方法。全部函數都有 apply 方法,它會將它接收的第一個參數設置爲函數的上下文。

例如本例中咱們能夠改寫代碼成這樣子:

var pseudoWindow = {
    document: {
        URL: "I'm fake URL"
    },

    getUrl1: getUrl,

    getUrl2: function (callback) {
        callback();        
        callback.apply(this);
    }
}

嚴格地說,你應該先檢查 callback 參數類型是不是函數對象。

總結

Javascript 支持將函數做爲參數傳遞,回調函數變量指向的函數對象都未與任何上下文綁定,全部未與明確上下文綁定的變量都是自由變量,瀏覽器器中全部自由變量的上下文都是 window 對象。

相關文章
相關標籤/搜索