任何變量或對象都有其賴以生存的上下文。若是簡單地將對象理解爲一段代碼,那麼對象處在不一樣的上下文,這段代碼也會執行出不一樣的結果。瀏覽器
例如,咱們定義一個函數 getUrl
和一個對象 pseudoWindow
。app
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 URL
。this
執行 pseudoWindow.getUrl2(getUrl)
,先打印出當前頁面 URL,後打印 I'm fake URL
。url
下面讓咱們用最簡單粗暴的語言來解釋以上代碼。code
this 就是函數調用使用的上下文。對象
上下文是在句號標記法中,句號前面的那個東西。ip
例如 pseudoWindow.getUrl1
,pseudoWindow
是 pseudoWindow.getUrl1()
的上下文。作用域
當一個變量沒有綁定到任何上下文時(或者說綁定到頂級做用域時,例如瀏覽器中的 window),它就是自由變量。get
變量就是代碼中你所用的標識符,一個標識符就是一個變量,多個變量可能指向同一個對象。例如:
pseudoWindow.getUrl1 === getUrl // 獲得 true
變量所處的上下文就是對象的做用域。
首先 getUrl
函數是定義在全局環境中,它是一個自由變量,在瀏覽器中(如下描述均爲瀏覽器環境)它的上下文就是 window
,因此 window.getUrl()
和 getUrl()
是等價的。所以 this
指向 window
對象,打印出當前 URL。
首先 pseudoWindow
是一個對象,它能夠充當上下文角色。咱們給它定義了一個屬性 getUrl1
,你能夠將屬性視爲被綁定到某個上下文的變量,變量 getUrl1
自己又指向了變量 getUrl
所指向的對象,因此 pseudoWindow.getUrl1 === getUrl
纔會爲 true
。
當咱們調用 pseudoWindow.getUrl1()
時,它的意思是執行 getUrl()
這段代碼,執行代碼所需的參數爲空,上下文爲 pseudoWindow
。
因此函數中的 this
指向了 pseudoWindow
,而 pseudoWindow
對象剛好又有 document
屬性,該屬性剛好又有 URL
屬性,所以打印出 I'm fake URL
。
同理咱們又定義了一個變量 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
,若是咱們想要經過回調的方式打印出 pseudoWindow
的 document.URL
屬性,又不想對 pseudoWindow
對象形成任何影響,那麼咱們可使用函數的 apply
方法。全部函數都有 apply
方法,它會將它接收的第一個參數設置爲函數的上下文。
例如本例中咱們能夠改寫代碼成這樣子:
var pseudoWindow = { document: { URL: "I'm fake URL" }, getUrl1: getUrl, getUrl2: function (callback) { callback(); callback.apply(this); } }
嚴格地說,你應該先檢查 callback 參數類型是不是函數對象。
Javascript 支持將函數做爲參數傳遞,回調函數變量指向的函數對象都未與任何上下文綁定,全部未與明確上下文綁定的變量都是自由變量,瀏覽器器中全部自由變量的上下文都是 window 對象。