前言
- this既不指向自身,也不指向函數的詞法作用域。
- this的綁定不是在編寫代碼的時候綁定的,而是在它運行的時候進行的。
- this的綁定和函數的聲明位置沒有任何關係,只取決於函數的調用方式。
this的綁定規則
this有四種綁定規則:
- 默認綁定
- 隱式綁定
- 顯示綁定
- new綁定
讓我們先來解釋一下這四個規則。
默認綁定
函數的獨立調用是函數最常見的一種調用方式,即直接使用不帶任何修飾的函數引用進行的調用,此時函數中的this會默認綁定到全局對象。(在非嚴格模式下,嚴格模式會綁定到undefined)。這就是默認綁定。
請看以下代碼:
function foo () {
console.log(this.a);
}
var a = 2;
foo();//2
此時的foo()直接使用的是不加任何修飾的函數引用的調用,因此函數中的this默認綁定到全局對象。
如果是嚴格模式,就會報錯。
function foo() {
"use strict"
console.log(this.a);
}
var a = 2;
foo(); // TypeError:this is undefined
隱式綁定
當函數的調用存在某個上下文對象時,函數中的this就會綁定在這個上下文對象上。即函數被某個對象當做引用屬性被添加到對象中時。
請看下面的代碼:
function foo () {
console.log(this.a);
}
var obj = {
a:2,
foo:foo,
}
obj.foo(); // 2
foo()的調用不在是獨立函數調用的方式,它的前面加上了對象obj的引用,此時函數內的this就會綁定在這個對象上,this.a和obj.a是一樣的。
注意
在函數調用的時候,前面如果出現對象引用鏈,函數中的this只會綁定到引用鏈的最後一個對象上。
代碼示例:
function foo () {
console.log(this.a);
}
var obj1 = {
a:42,
foo:foo,
}
var obj2 = {
a:2,
obj1:obj1,
}
obj2.obj1.foo(); // 42
顯示綁定
顯示綁定並不像隱式綁定那樣,必須在對象內部包含一個指向函數的引用屬性,並通過這個屬性間接的引用函數。顯示綁定則不需要在對象內部包含函數引用,也能強制的調用函數。
JavaScript中可以使用函數的兩個方法來實現這個——apply( )和call( )。
這兩個方法是如何工作的呢?它們的第一個參數是一個對象,這個對象是給this準備的,接着在調用函數的時候,將this綁定到這個對象上,因爲你可以指定要綁定的對象,所有該綁定稱爲顯示綁定。
代碼演示:
function foo () {
console.log(this.a);
}
var obj = {
a:2,
};
foo.apply( obj ); // 2
foo.call( obj ); // 2
這兩個方法在this的綁定上是一樣的,它們的區別是在其他的參數上,(當然我們不在這裏討論它倆的區別),foo()通過這兩個方法可以將this綁定在所傳入的對象上。
new綁定
在使用new來調用函數或者是構造函數的調用時,會發生以下四個步驟:
- 創建一個新對象
- 這個新對象會被執行[[Prototype]]鏈接
- 這個新對象會綁定函數的this。
- 如果函數沒有返回其他的對象,那麼new表達式中的函數調用就返回這個新對象。
在這裏我們關心的是第1,3,4步,至於第2步,暫時先跳過。
考慮下面的代碼:
function foo (a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2
使用new 來調用foo(…)時,會構建一個新的對象,並將this綁定到這個新對象上,最後返回這個新對象。new是最後一種可以影響this綁定行爲的方法,稱之爲new綁定。
綁定規則的優先級
現在我們已經瞭解了這四種綁定規則,但當一個函數運用多種綁定規則時該怎麼辦呢?因此我們現在來測試綁定規則的優先級的問題。
這就是綁定規則的優先級問題,而綁定規則的有限次序是:
new綁定 > 顯示綁定 > 隱式綁定 > 默認綁定
總結
現在我們可以通過綁定規則的優先級來進行判斷this的綁定對象:
函數如果是在new中調用,那麼this就會綁定在新創建的對象的上。
var bar = new foo()
函數是否apply,call來調用,如果是的話,this就綁定到那個指定的對象上。
var bar = foo.apply(obj)
函數是否在某個上下午中調用,如果是的話,this就綁定到那個上下文對象上。
var bar = obj.foo()
如果以上都不是的話,那就是默認綁定,如果在嚴格模式下,this綁定的是undefined,如果是非嚴格模式下,this綁定的是全局對象。
var bar = foo()
this的綁定就是這樣了。