this 是很多人會混淆的概念,但是其實它一點都不難,只是網上很多文章把簡單的東西說複雜了。在這一小節中,你一定會徹底明白 this 這個概念的。
this的指向在函式定義的時候是確定不了的,只有函式執行的時候才能確定this到底指向誰,實際上this的最終指向的是那個呼叫它的物件(這句話有些問題,後面會解釋為什麼會有問題,雖然網上大部分的文章都是這樣說的,雖然在很多情況下那樣去理解不會出什麼問題,但是實際上那樣理解是不準確的,所以在你理解this的時候會有種琢磨不透的感覺)
例子1:
按照我們上面說的this最終指向的是呼叫它的物件,這裡的函式a實際是被window物件所點出來的,下面的**就可以證明。
看到a可以通過window點出來,說明this指向的是window
例子:
這裡的this指向的是物件o,因為你呼叫這個fn是通過o.fn()執行的,那自然指向就是物件o,這裡再次強調一點,this的指向在函式建立的時候是決定不了的,在呼叫的時候才能決定,誰呼叫的就指向誰,一定要搞清楚這個。 其實例子1和例子2說的並不夠準確,下面這個例子就可以推翻上面的理論。 如果要徹底的搞懂this必須看接下來的幾個例子
這段**和上面的那段**幾乎是一樣的,但是這裡的this為什麼不是指向window,如果按照上面的理論,最終this指向的是呼叫它的物件,這裡先說個而外話,window是js中的全域性物件,我們建立的變數實際上是給window新增屬性,所以這裡可以用window點o物件。
這裡先不解釋為什麼上面的那段**this為什麼沒有指向window,我們再來看一段**。
這裡同樣也是物件o點出來的,但是同樣this並沒有執行它,那你肯定會說我一開始說的那些不就都是錯誤的嗎?其實也不是,只是一開始說的不準確,接下來我將補充一句話,我相信你就可以徹底的理解this的指向的問題。
情況3:如果乙個函式中有this,這個函式中包含多個物件,儘管這個函式是被最外層的物件所呼叫,this指向的也只是它上一級的物件,例子3可以證明,如果不相信,那麼接下來我們繼續看幾個例子。
儘管物件b中沒有屬性a,這個this指向的也是物件b,因為this只會指向它的上一級物件,不管這個物件中有沒有this要的東西。
還有一種比較特殊的情況,例子:
這裡this指向的是window,是不是有些蒙了?其實是因為你沒有理解一句話,這句話同樣至關重要。
this永遠指向的是最後呼叫它的物件,也就是看它執行的時候是誰呼叫的,例子4中雖然函式fn是被物件b所引用,但是在將fn賦值給變數j的時候並沒有執行所以最終指向的是window,這和例子3是不一樣的,例子3是直接執行了fn。
this講來講去其實就是那麼一回事,只不過在不同的情況下指向的會有些不同,上面的總結每個地方都有些小錯誤,也不能說是錯誤,而是在不同環境下情況就會有不同,所以我也沒有辦法一次解釋清楚,只能你慢慢地的去體會。
建構函式版this:
這裡之所以物件a可以點出函式fn裡面的user是因為new關鍵字可以改變this的指向,將這個this指向物件a,為什麼我說a是物件,因為用了new關鍵字就是建立乙個物件例項,理解這句話可以想想我們的例子3,我們這裡用變數a建立了乙個fn的例項(相當於複製了乙份fn到物件a裡面),此時僅僅只是建立,並沒有執行,而呼叫這個函式fn的是物件a,那麼this指向的自然是物件a,那麼為什麼物件a中會有user,因為你已經複製了乙份fn函式到物件a中,用了new關鍵字就等同於複製了乙份。
說完了以上幾種情況,其實很多**中的 this 應該就沒什麼問題了,下面讓我們看看箭頭函式中的 this
function a() } }
console.log(a()()())
首先箭頭函式其實是沒有 this 的,箭頭函式中的 this 只取決包裹箭頭函式的第乙個普通函式的 this。在這個例子中,因為包裹箭頭函式的第乙個普通函式是 a,所以此時的 this 是 window。另外對箭頭函式使用 bind 這類函式是無效的。
最後種情況也就是 bind 這些改變上下文的 api 了,對於這些函式來說,this 取決於第乙個引數,如果第乙個引數為空,那麼就是 window。
那麼說到 bind,不知道大家是否考慮過,如果對乙個函式進行多次 bind,那麼上下文會是什麼呢?
let a = {}
let fn = function ()
fn.bind().bind(a)() 指向window // => ?複製**
如果你認為輸出結果是 a,那麼你就錯了,其實我們可以把上述**轉換成另一種形式
可以從上述**中發現,不管我們給函式 bind 幾次,fn 中的 this 永遠由第一次 bind 決定,所以結果永遠是 window。
let a = function foo() foo.bind(a)() // => 'yck'複製**
以上就是 this 的規則了,但是可能會發生多個規則同時出現的情況,這時候不同的規則之間會根據優先順序最高的來決定 this 最終指向**。
更新乙個小問題當this碰到return時:
再看乙個
什麼意思呢?
如果返回值是乙個物件,那麼this指向的就是那個返回的物件,如果返回值不是乙個物件那麼this還是指向函式的例項。
還有一點就是雖然null也是物件,但是在這裡this還是指向那個函式的例項,因為null比較特殊。
知識點補充:
1.在嚴格版中的預設的this不再是window,而是undefined。
首先,new 的方式優先順序最高,接下來是 bind 這些函式,然後是 obj.foo() 這種呼叫方式,最後是 foo 這種呼叫方式,同時,箭頭函式的 this 一旦被繫結,就不會再被任何方式所改變。
js 執行上下文,this指向
執行上下文的建立過程 執行 之前,進入建立上下文階段 初始化作用域鏈 建立變數物件 1.建立argumens物件,初始化引數名稱和值並建立引用的複製 2.掃瞄上下文的函式宣告 而非函式表示式 為發現的每乙個函式,在變數物件上建立乙個屬性,屬性名就是函式的名字,儲存乙個函式在堆記憶體中的引用 如果該屬...
js執行上下文與執行上下文棧
在了解js的執行上下文物件與執行上下文棧之前,我們要先了解兩個概念 即變數提公升跟函式提公升 變數提公升 通過var定義的變數,在定義語句之前我們就可以直接訪問到,不過它的值是undefined 函式提公升 通過function定義的函式,在函式定義語句前,我們就可以直接呼叫 變數提公升與函式提公升...
JS執行上下文
執行上下文,即context,也不知道是誰翻譯的,不少的文獻 書籍用的都是這個詞。還記得第一次接觸這個詞時的惆悵 迷惘 不知所措,扶了扶眼鏡,翻開大辭典,還是翻譯成環境比較接地氣。js執行上下文,即js的執行環境。當我們的 執行時,會進入到不同的執行上下文,即不同的環境。在不同的環境中,有著不同的 ...