非原創:
想要確定this的指向,需要分下列幾種情況進行討論:
全局代碼中的this
函數中的this
其中函數中的this比較復雜,也更為常見,下面會著重說明。
全局代碼中的 THIS
這個很好理解,就是指向全局對象window,如果是嚴格模式則是undefined,下面為了簡便,直接用global代替,不再對是否是嚴格模式進行區分。
this.a = 10
console.log(a) // 10
b = 20
console.log(this.b) // 20
var c = 30
console.log(this.c) // 30
函數中的 THIS
先上結論:
函數上下文中的this值由調用者提供,并由表達式的形式確定;
如果在調用括號的左側存在引用類型的值,則將this設置為該引用類型的base對象;
在所有其他情況下,this的值始終設置為null,因為null沒有意義,所以被隱式轉換為全局對象。
結論中的細節下面將會介紹。
引用類型
為了理解方便,先介紹一個基本概念:
引用類型可以表示為對象,base是屬性所屬的對象,propertyName是此對象中屬性的名字,strict表示是否為嚴格模式。
var foo = 10
function bar() {}
可以理解成:
var fooReference = {
? base: global,
? propertyName: 'foo'
}
var barReference = {
? base: global,
? propertyName: 'bar'
}
OK,那么下面再來兩個 融會貫通一下。
function foo() {
? ? console.log(this)
}
foo() // global
var fooReference = {
? ? base: global,
? ? propertyName: 'foo'
}
結論很顯然,括號左邊是foo函數,即window.foo(),引用類型,那么this指向base,即global。
擴展一下:
foo.prototype.constructor() // foo.prototype
var fooPrototypeConstructorReference = {
? base: foo.prototype,
? property?Name: 'constructor'
}
同樣的判斷方法~
再來一個 :
function foo() {
? console.log(this.bar)
}
var x = { bar: 10 }
var y = { bar: 20 }
x.test = foo
y.test = foo
x.test() // 10
y.test() // 20
var xTestReference = {
? base: x,
? propertyName: 'test'
}
// y應該不用再寫了吧
非引用類型
非引用類型就一句話:全局對象。
老規矩,舉 :
(function() {
? console.log(this) // null => global
})()
// 括號內?為函數聲明,并非引用
下面的? 可能會讓人困惑:
var foo = {
? bar: function() {
? ? console.log(this)
? }
}
foo.bar() // Reference, OK => foo
(foo.bar)() // Reference, OK => foo
(foo.bar = foo.bar)() // global
(false || foo.bar)() // global
(foo.bar, foo.bar)() // global
后三組由于進行?了賦值和運算,因此base丟失,他們其實都變成了函數聲明,因此類似于自執行函數,this指向global。
另外兩種情況
還有兩種情況,如果作為構造函數被調用,指向實例化對象;如果是call和apply調用,指向?他們的參數。
一點想法
this的指向確實是個比較?麻煩的點,理解上不困難,但是有些點需要記憶,我覺得比較合適的方式是,在用到過程中學習this,比如react中為什么要在constructor中?bind(this),為什么Vue的組件中,data中的數據要寫在return中,通過這種方式具體的學習?this更加實際。