前言
相信我,只要記住本文的 7步口訣,就能徹底掌握 JS 中的 this 指向。
先念口訣:箭頭函數、new、bind、apply 和 call、歐比屆點(obj.)、直接調用、不在函數里。
按照口訣的順序,只要滿足前面某個場景,就可以確定 this 指向了。
接下來按照口訣順序對它們進行詳解,文中示例代碼都運行在 Chrome 的 Console 控制臺中。
文末有精心準備的練習題,用于檢驗學習成果,別忘了試試~
1. 箭頭函數
箭頭函數排在第一個是因為它的 this 不會被改變,所以只要當前函數是箭頭函數,那么就不用再看其他規(guī)則了。
箭頭函數的 this 是在創(chuàng)建它時外層 this 的指向。這里的重點有兩個:
- 創(chuàng)建箭頭函數時,就已經確定了它的 this 指向。
- 箭頭函數內的 this 指向外層的 this。
所以要知道箭頭函數的 this 就得先知道外層 this 的指向,需要繼續(xù)在外層應用七步口訣。
2. new
當使用 new 關鍵字調用函數時,函數中的 this 一定是 JS 創(chuàng)建的新對象。
讀者可能會有疑問,“如果使用 new 關鍵調用箭頭函數,是不是箭頭函數的 this 就會被修改呢?”。
我們在控制臺試一下。
1
2
|
func = () => {} new func() // throw error |
從控制臺中可以看出,箭頭函數不能當做構造函數,所以不能與 new 一起執(zhí)行。
3. bind
bind 是指 Function.prototype.bind() 。
多次 bind 時只認第一次 bind 的值
易錯點
1
2
3
4
5
|
function func() { console.log( this ) } func.bind(1).bind(2)() // 1 |
箭頭函數中 this 不會被修改
1
|
|
bind 與 new
易錯點
1
2
3
4
5
6
|
function func() { console.log( this , this .__proto__ === func.prototype) } boundFunc = func.bind(1) new boundFunc() // Object true,口訣 2 優(yōu)先 |
4. apply 和 call
apply() 和 call() 的第一個參數都是 this,區(qū)別在于通過 apply 調用時實參是放到數組中的,而通過 call 調用時實參是逗號分隔的。
箭頭函數中 this 不會被修改
易錯點
1
2
3
4
5
6
|
func = () => { // 這里 this 指向取決于外層 this,參考口訣 7 「不在函數里」 console.log( this ) } func.apply(1) // Window,口訣 1 優(yōu)先 |
bind 函數中 this 不會被修改
易錯點
1
2
3
4
5
6
|
function func() { console.log( this ) } boundFunc = func.bind(1) boundFunc.apply(2) // 1,口訣 3 優(yōu)先 |
5. 歐比屆點(obj.)
1
2
3
4
5
6
7
|
function func() { console.log( this .x) } obj = { x: 1 } obj.func = func obj.func() // 1 |
這里就不用代碼例證箭頭函數和 bind 函數的優(yōu)先級更高了,有興趣可自行嘗試吧。
6. 直接調用
在函數不滿足前面的場景,被直接調用時,this 將指向全局對象。在瀏覽器環(huán)境中全局對象是 Window,在 Node.js 環(huán)境中是 Global。
先來個簡單的例子。
1
2
3
4
5
|
function func() { console.log( this ) } func() // Window |
來一個復雜的例子,外層的 outerFunc 就起個迷惑目的。
1
2
3
4
5
6
7
8
9
10
11
|
function outerFunc() { console.log( this ) // { x: 1 } function func() { console.log( this ) // Window } func() } outerFunc.bind({ x: 1 })() |
7. 不在函數里
不在函數中的場景,可分為瀏覽器的 <script /> 標簽里,或 Node.js 的模塊文件里。
- 在 <script /> 標簽里,this 指向 Window。
- 在 Node.js 的模塊文件里,this 指向 Module 的默認導出對象,也就是 module.exports。
非嚴格模式
嚴格模式是在 ES5 提出的。在 ES5 規(guī)范之前,也就是非嚴格模式下,this 不能是 undefined 或 null。所以**在非嚴格模式下,通過上面七步口訣,如果得出 this 指向是 undefined 或 null,那么 this 會指向全局對象。**在瀏覽器環(huán)境中全局對象是 Window,在 Node.js 環(huán)境中是 Global。
例如下面的代碼,在非嚴格模式下,this 都指向全局對象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function a() { console.log( "function a:" , this ) ;(() => { console.log( "arrow function: " , this ) })() } a() a.bind( null )() a.bind(undefined)() a.bind().bind(2)() a.apply() |
非嚴格模式下執(zhí)行結果為:
在嚴格模式下,執(zhí)行同樣的代碼進行對比。記住要一次性將所有代碼復制粘貼到控制臺中,才能運行在嚴格模式下(因為第一行 "use strict" 才會對后面的代碼生效)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
"use strict" function a() { console.log( "function a:" , this ) ;(() => { console.log( "arrow function: " , this ) })() } a() a.bind( null )() a.bind(undefined)() a.bind().bind(2)() a.apply() |
嚴格模式下執(zhí)行結果為:
七步口訣在嚴格模式下和非嚴格模式下都是完備的,只是在非嚴格模式下 null 或 undefined 會被轉換為全局對象。所以我沒有將這點列入口訣中。
做題復習
先背誦口訣再做題,“箭頭函數、new、bind、apply 和 call、歐比屆點(obj.)、直接調用、不在函數里”。
1. 下面代碼執(zhí)行后,func.count 值為多少?
1
2
3
4
5
6
|
function func(num) { this .count++ } func.count = 0 func(1) |
答案
func.count 值為 0。
按照口訣,func() 調用時屬于第 6 類「直接調用」。在非嚴格模式下,this 指向全局對象。this 跟 func 一點關系都沒有,所以 func.count 保持不變。so easy。
2. 以下箭頭函數中 this 指向誰呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
obj = { func() { const arrowFunc = () => { console.log( this ._name) } return arrowFunc }, _name: "obj" , } obj.func()() func = obj.func func()() obj.func.bind({ _name: "newObj" })()() obj.func.bind()()() obj.func.bind({ _name: "bindObj" }).apply({ _name: "applyObj" })() |
答案
// obj
// undefined
// newObj
// undefined
// bindObj
是不是很簡單,你學廢了嗎?
總結
到此這篇關于JavaScript中的this指向問題的文章就介紹到這了,更多相關js中this指向內容請搜索服務器之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://juejin.cn/post/6946021671656488991