JavaScript 中的 this 指向問題有很多博客在解釋,仍然有很多人問。
與我們常見的很多語言不同,JavaScript 函數中的 this 指向并不是在函數定義的時候確定的,而是在調用的時候確定的。換句話說,函數的調用方式決定了 this 指向。
JavaScript 中,普通的函數調用方式有三種:直接調用、方法調用和 new 調用。除此之外,還有一些特殊的調用方式,比如通過 bind() 將函數綁定到對象之后再進行調用、通過 call()、apply() 進行調用等。而 es6 引入了箭頭函數之后,箭頭函數調用時,其 this 指向又有所不同。下面就來分析這些情況下的 this 指向。
1 直接調用
直接調用,就是通過 函數名(...) 這種方式調用。這時候,函數內部的 this 指向全局對象,在瀏覽器中全局對象是 window,在 NodeJs 中全局對象是 global。
這里需要注意的一點是,直接調用并不是指在全局作用域下進行調用,在任何作用域下,直接通過 函數名(...) 來對函數進行調用的方式,都稱為直接調用。
bind() 對直接調用的影響
還有一點需要注意的是 bind() 的影響。Function.prototype.bind() 的作用是將當前函數與指定的對象綁定,并返回一個新函數,這個新函數無論以什么樣的方式調用,其 this 始終指向綁定的對象。
call 和 apply 對 this 的影響
上面的示例中用到了Function.prototype.apply(),與之類似的還有Function.prototype.call()。這兩方法的用法請大家自己通過鏈接去看文檔。不過,它們的第一個參數都是指定函數運行時其中的 this 指向。
不過使用 apply 和 call 的時候仍然需要注意,如果目錄函數本身是一個綁定了 this 對象的函數,那 apply 和 call 不會像預期那樣執行
2 方法調用
方法調用是指通過對象來調用其方法函數,它是 對象.方法函數(...) 這樣的調用形式。這種情況下,函數中的 this 指向調用該方法的對象。但是,同樣需要注意 bind() 的影響。
再次強調,函數內部的 this 指向與定義無關,受調用方式的影響。
方法中 this 指向全局對象的情況
注意這里說的是方法中而不是方法調用中。方法中的 this 指向全局對象,如果不是因為 bind(),那就一定是因為不是用的方法調用方式
3 new 調用
在 es6 之前,每一個函數都可以當作是構造函數,通過 new 調用來產生新的對象(函數內無特定返回值的情況下)。而 es6 改變了這種狀態,雖然 class 定義的類用 typeof 運算符得到的仍然是 "function",但它不能像普通函數一樣直接調用;同時,class 中定義的方法函數,也不能當作構造函數用 new 來調用。
箭頭函數沒有自己的 this 綁定。箭頭函數中使用的 this,其實是直接包含它的那個函數或函數表達式中的 this。
而在 es5 中,用 new 調用一個構造函數,會創建一個新對象,而其中的 this 就指向這個新對象。這沒有什么懸念,因為 new 本身就是設計來創建新對象的。
另外需要注意的是,箭頭函數不能用 new 調用,不能 bind() 到某個對象(雖然 bind() 方法調用沒問題,但是不會產生預期效果)。不管在什么情況下使用箭頭函數,它本身是沒有綁定 this 的,它用的是直接外層函數(即包含它的最近的一層函數或函數表達式)綁定的 this。