JavaScript中的this到底指向誰?
很多初學JavaScript的小夥伴,都找不準函式或者方法中的this到底指向誰,其實並沒有那麼複雜,只需要記住一個口訣就能正確找到this的指向:不管函式或者方法是如何宣告的,要看這個函式或者方法最終是誰呼叫的,**誰最終呼叫這個函式或方法,那麼這個函式或方法中的this就是誰**.
下面從函式呼叫方式著手詳細給大家介紹一下這個口訣.
1. 作為普通函式中的this
示例程式碼
大家都知道寫一個全域性函式相當於給window物件添加了一個方法,所以第5行呼叫welcome函式和第6行呼叫welcome函式本質是一樣的,所以welcome函式最終是window物件在呼叫,所以welcome函式內部的this就指向了window物件.執行結果如下:
2. 作為物件的方法中的this
示例程式碼
最後1行的sayHi方法是由obj物件點出來呼叫的,所以sayHi方法中的this指向obj物件.所以輸出的是obj物件中的name的值, 結果如下:
3. 綜合案例
3.1 案例一
這裡要搞明白的是第15行程式碼的含義, 這句話的作用是把obj物件裡面的sayHi函式的函式體賦值給welcome, 所以在第16行呼叫welcome函式的時候,執行的程式碼其實是obj中sayHi的函式體. 那麼執行的就是第10、11行, 那麼這個this是誰呢? 其實很簡單,不要看這個方法是宣告在obj物件裡面的, 要看這個方法是在第16行呼叫,是window呼叫的,所以此時這個this是window,輸出的結果就是window的name屬性,就是那個全域性name的值, 結果如下:
3.2 案例二
這裡同樣要搞明白第15行程式碼的含義,這句話的意思是把welcome的函式體賦值給obj物件的sayHi函式, 所以在第16行呼叫obj物件的sayHi函式實際上執行的程式碼是第2行第3行, 那第3行的this是誰呢? 此時不要看這個welcome是怎麼宣告的, 要看這個程式碼是在第16行呼叫的,是obj物件點出來呼叫的,所以this指向obj.結果如下:
3.1 案例三
最後一行這裡給人的錯覺好像sayHi方法是p1點出來呼叫的,事實這種想法是錯誤的,p1點出來的只是dog物件, sayHi方法還是由dog點出來呼叫的,所以sayHi方法中的this還是指向dog物件.所以輸出的結果如下:
4. 建構函式中的this
4.1 直接呼叫建構函式
大家注意看第6行,呼叫Student函式的時候沒有用new關鍵字,那麼這就意味著Student函式是window點出來呼叫的,所以Student函式中的this就是window,那麼第2行第3行就是在給window新增name和age屬性,值分別是黑馬和12. 然後函式內部沒有寫return關鍵字那預設返回值是undefined.所以s1接受到的是undefined. 整個輸出結果如下:
4.2 使用new呼叫建構函式
大家都知道new關鍵字會建立一個物件,並且會把建構函式中的this指向這個物件,並且還會把這個物件自動返回, 那麼執行完第6行程式碼, s1就是那個new關鍵字建立並返回的物件.在第2行第3行也已經給這個物件添加了name和age屬性. 所以上面的程式碼執行結果如下:
4.3 為建構函式手動return後的情況
這裡再來看看如果給建構函式添加了return關鍵字會如何,程式碼如下
說到這裡大家就要注意,因為new關鍵字會自動幫我們返回他建立的物件,所以一般在建構函式中是不用寫return返回值的, 那萬一建構函式中就是寫了return返回值會怎麼樣呢?
這個時候就要看return關鍵字後面返回的值是什麼型別的,如果返回的是數值型別/字串型別/布林型別/null/undefined,那麼會忽略這個返回值. 如果返回的值不是這些型別的,比如是一個數組或者一個物件,那麼這個返回值會覆蓋原來new關鍵字自動返回的那個物件. 所以上面程式碼執行的結果如下:
但是建構函式中的this還是new關鍵字建立的那個物件(驗證看第10行輸出的結果), 只是這個物件返回的時候被return關鍵字返回的陣列給覆蓋了,所以s1接受不到而已.
5. 修改函式中this的預設指向
以上我們討論的是,預設情況下,函式中this的指向。實際上JavaScript提供了一套機制,允許我們對函式中的this指向進行修改,可以讓函式中的this指向我們想要讓他指向的物件。
大家現在都知道上述程式碼中welcome函式中this指向window物件, 輸出的是window, 但是我們現在想讓welcome函式中的this指向obj物件,那應該怎麼做呢?
肯定是不能直接修改函式中this的指向的,直接修改會報錯, 因為this被JavaScript設計為只讀,程式碼如下:
這個時候,要實現這個效果,要想讓welcome函式中的this指向obj, 就要使用函式上下文呼叫模式,比如call、 apply、 bind。
這個時候,welcome函式被呼叫,但是welcome函式內部的this就指向obj物件了。