this
所指向是物件是看我們如何呼叫函式。不論在全域或函式裏,都會自動帶上this
這個關鍵詞,我們可以調用它來指向某個物件。當我們在全域使用this
,它會指向window
這個物件。但當我們呼叫函式時,this
會因應我們「如何呼叫」函式而被影響。
影響this
的幾種情況:
我們為什麼需要用this
呢?其中一個常見的例子就是我們有一個多層的物件,而我們想在物件較內部的執行環境可以取到較外部的資料。
關於this
的課題,最常見的情況是在物件裏調用函式,而該函式的this
會指向該物件:
var name = 'Hong Kong'
function travel(){
console.log(this.name)
}
var city = {
name: 'Taipei',
travel: travel
}
city.travel() //Taipei
travel() //Hong kong
以上例子可見我們有2種不同方式來呼叫travel()
,結果都是不同。我們只需要看travel()
這個函式是如何被呼叫。第一個,我們是在city
物件裏呼叫,所以this
會指向city
物件,name
就是在city
物件裏的Taipei。
可是,當在全域呼叫函式travel
,這時候,travel
裏的this
就會指向全域物件(window)的name
屬性,因此回傳Hong Kong
。
沒錯,travel
是在全域宣告,但我們不用理會函式travel
是在哪裏宣告。只需要理會我們如何呼叫travel
。
那麼如果我們把city.travel()
這個呼叫方式賦予到一個變數裏,再執行該變數,我們也會得出Taipei
嗎?就像以下的做法:
var name = 'Hong Kong'
function travel(){
console.log(this.name)
}
var city = {
name: 'Taipei',
travel: travel
}
//拷貝該函式到變數a
var a = city.travel
a()//Hong Kong
答案是不會。因為這裏的a
只是拷貝了city.travel
裏的那個函式,即是travel
。所以a()
是指我們直接呼叫travel
函式。
簡單呼叫(Simple call)就是直接調用函式,函式裏的this
會指向全域的window
。但是一般而言都不建議使用這個方法,在文章較後的部分會再解釋。
以下的例子很直接,我們直接呼叫travel
,travel
裏的this
是指向全域的window
,因此會回傳全域變數name
的值,Hong Kong。
var name = 'Hong Kong'
function travel(){
console.log(this.name)
}
travel(); //Hong Kong
再看看以下例子,雖然有兩層函式,但我們都是直接呼叫travel
,所以結果同樣是指向全域的Taipei。
var name = 'Taipei'
function travel(){
console.log(this.name)
function innerTravel(){
console.log(this.name)
}
innerTravel()
}
travel()
//Taipei
//Taipei
以閉包函式為例,道理也是相同的,我們也是直接在全域呼叫函式,this
會指向window物件。
var myScore = 3000
function calculate(score){
var myScore = score;
return function(addScore){
myScore += addScore;
console.log(myScore) //500+1000 = 1500
console.log(this.myScore) //3000(全域)
}
}
var result = calculate(500);
result(1000) //也是直接呼叫函式
除此之外,在Callback function的情況中,函式裏所指向的this
同樣都會是全域window
物件,所謂的callback function就是把一個函式傳到另一個函式裏,並在該函式裏執行,例如我們經常會用到的forEach
方法:
var x = [1,2,3]
x.forEach(function(num){
console.log(this) //window物件 x3
})
然而,其實不建議用簡易呼叫的方式去調用this
,因為在嚴格模式下會出現錯誤,例如以下例子:
var a = 100
function test(){
'use strict'
console.log(this.a)
}
//這樣寫會報錯
test()
//Uncaught TypeError: Cannot read property 'a' of undefined
test.call(this) //100
我們知道這裏的this
是指向window
,但是有時在嚴格模式下,我們用簡易呼叫的方式,this
會被當成undefined
而報錯。我們需要用到call
、apply
等方法,刻意把this
傳進去,即把window
物件帶進去。依照call
的方法,現在函式test
裏的this
就可以成功指向window
。
bind
、apply
、call
的方法非常相似,這些方法的第一個参數是this會指向的物件。
差異點在於:
call
和bind
第二及之後的参數都是一個個值apply
第二個参數必須是陣列call
和apply
會立即執行,但bind
需要在被呼叫後才會執行應用例子:
var num = 999
var obj = {
num: 10
}
function showNum(a,b){
console.log(this.num, a, b)
}
//this是window,回傳是在全域的num
showNum(20,30) //999,20,30
//call方法
//this是obj,回傳是obj的num
showNum.call(obj,20,30) //10,20,30
//apply方法
showNum.apply(obj,[20,30]) //10,20,30
//bind方法
//不會自動執行,需要被呼叫
var newNum = showNum.bind(obj,20,30);
newNum() //10,20,30
使用bind
時要注意,當我們用bind
方法傳入参數後,我們要手動呼叫這個方法。當我們呼叫它時並不能再次傳入参數,例如以下情況,newNum
裏的2000和3000會被無視:
var newNum = showNum.bind(obj,20,30);
newNum(2000,3000)
//最後回傳結果仍然是 10,20,30
另外,如果第一個参數是undefined
或null
,this
就會指向window物件,看看以下例子:
var obj = {
num: 10
}
function showNum(a,b){
console.log(this.num, a, b)
}
showNum.call(null,10,20) //undefined, 10, 20
showNum.call(undefined,50,70)//undefined, 10, 20
showNum.apply(null,[90,100])//undefined, 90, 100
var x = showNum.bind(undefined, 40,40)
x() //undefined, 40, 40
當我們把null
和undefined
傳進去函式當作this
参考的物件時,this
就會指向window
,因為全域並沒有num
這個變數,所以我們會得出undefined
。試想想,如果我們剛好有一個全域變數var num = 3
,那麼全部結果都會回傳3了。
如果第一個参數是基本型別值,這個值會被封裝成物件:
function showNum(a,b){
console.log(this, a, b)
}
showNum.call(200,1,2) //Number {200}, 1, 2
showNum.call('200',1,2) //String {"200"}, 1, 2
在原型鏈的章節中,我們知道可以用建構函式產生出一個實體物件。在建構函式中的this
,會指向那個新物件:
//建構函式
function createObj(){
this.prop1 = 123
this.prop2 = 'hello'
}
//實體物件
var obj = new createObj();
console.log(obj) //{prop1: 123, prop2: "hello"}
在以上例子可見,建構函式裏的this
不是指向window
,而是指向obj
這個剛剛被建立出來的物件。因此,該新物件就會被設定prop1
和prop2
屬性和值。
this
,會指向window
物件this
的幾種情況:物件裏的方法、簡單呼叫(不建議用)、bind, apply, call方法、new 建構函式、箭頭函式(先不在文章討論)this
會是undefined
bind
、call
、apply
的方法觀念相近,同樣是在第一個参數傳入一個物件,函式裏this
將會指向該物件bind
不會自動執行,需要被呼叫this
會指向由該建構函式產生的實體物件JS 原力覺醒 Day17 - this 的四種繫結
鐵人賽:JavaScript 的嚴格模式 "use strict"
鐵人賽:JavaScript 的 this 到底是誰?
JS核心篇(六角學院)