(Day-16) this 介紹上 - this 的指向
前言
不論是學習或是開發時,我們容易被 this 的指向搞的頭昏眼花,接下來會花兩個篇幅介紹 this 指向。
也因為 this 容易把人搞亂這邊先列出影響 this 指向的方法:
- 簡易呼叫 ( simple call)
- 物件函式呼叫
- addEventListener() 監聽事件觸發的函式
- bind ﹑apply 、 call 綁定方法
- 嚴格模式
- new 建構式
- 箭頭函式
本章節會先介紹上面 簡易呼叫、物件函式呼叫 ,addEventListener() 監聽事件觸發的函式這三個。
bind ﹑apply 、 call 綁定方法、嚴格模式 下篇則會說明這兩組狀況,剩餘的 new 建構式 、箭頭函式 則會到他們各自章節介紹。
簡易呼叫 ( simple call)
先來看看指向全域的(window) 的 this 寫法:
1
2
3
4
5var name = 'Ryder'
function showName() {
console.log(this.name)
}
showName()
這邊直接使用showName()
這種直接呼叫函式的方法,這種直接呼叫函式的方法我們稱做 簡易呼叫 ,只要是由簡易呼叫觸發的函式,他當中的this
一律指向window
。
可以看到範例中確實會顯示Ryder
,其實範例中this
指向的是window
。
順帶一題我們常用的forEach()
、filter()
中的 callback function ,他也是屬於 簡易呼叫 ,例如:
1
2
3
4
5
6
7
8
9
10
11
12var name = 'Ryder'
var array = [1,2,3]
array.forEach(function(){
console.log(this.name) // Ryder * 3
})
array.filter(function(){
console.log(this.name) // Ryder * 3
})
物件函式呼叫
再來看看this
指向物件的寫法:
1
2
3
4
5
6
7
8var name = 'Ryder';
var obj = {
name: 'Jack',
showName() {
console.log(this.name) // Jack
},
}
obj.showName()
這個範例中 this 指向的是obj
這個物件本身,關於物件函式的this
指向有一個小撇步, this 指向的位置就是,呼叫函式xxx()
的上一層物件 ,如圖:
在使用另一個多層物件來看看結果是否一致
1
2
3
4
5
6
7
8
9
10
11var name = 'Ryder';
var obj1 = {
name: 'Jack',
obj2: {
name: 'Alice',
showName() {
console.log(this.name) //Alice
},
}
}
obj1.obj2.showName()
在根據小撇步this
會是showName()
的上一層物件,圖片就會是
而答案也是正確的,範例中的this
指向的會是obj2
,因此這個範例顯示'Alice'
addEventListener() 監聽事件觸發的函式
1
2
3
4
5function eventFn(){
console.log(this) // DOM
}
const box = document.querySelector('.box')
box.addEventListener('click', eventFn)
當我們使用addEventListener
配合click
、mouseover
等等事件觸發的函式,他裡面的this
會是指向觸發事件的 DOM 本身,比如這個範例的點擊 box 時,this
就會是class="box"
的 DOM 元素:
但要注意的是,這個 DOM 指向是addEventListener()
特有的, 我們如果改成早期的onclick
寫法,則又會發現 this 指向的是window
(全域) 。
這個範例,完整程式碼可以參考 codepen :https://codepen.io/rider159159/pen/JjJrgjK
延伸範例
看到這裡你可能會想說this
的指向好像並不困難,上述都是建立在程式碼拆開來說的情況,接下來就以上面觀念,舉出一些容易讓人覺的混亂例子,並且在一一說明。
範例一:
1
2
3
4
5
6
7
8
9
10
11var name = 'Ryder'
var obj = {
name :'Jack',
showName() {
name = 'Alice'
console.log(this.name)
},
}
var a = obj.showName
a()
結果會是Alice
這是因為obj.showName
賦值給變數a
時,並沒有使用()
呼叫 ,因此是將showName
這個函式的整個內容,賦值到a
變數上,接者呼叫a()
來執行原本是showName
函式中的語法,因此這邊a()
是 簡易呼叫,所以this
會指向window
。
但是一執行a()
,函式中name = 'Alice'
這段語法就會將全域name
的值替換成Alice
,可以在瀏覽器打上name
來查看全域的name
是否被替換。
範例二:
1
2
3
4
5
6
7
8
9
10
11var name = 'Ryder'
var obj = {
name: 'Jack',
showName() {
name = 'Alice'
console.log(this.name)
},
}
var a = obj.showName()
a
這個結果就會是 Jack ,雖然有把obj.showName()
賦值到變數 a ,但這邊還是由obj.showName()
來呼叫showName()
函式,因此這個函式的狀況仍然如下圖,自然 this 的指向就會是obj
範例三:
1
2
3
4
5
6
7
8
9
10
11function eventFn(){
var name = 'Jack'
array.forEach(function(){
console.log(this.name)
})
}
var name = 'Ryder'
var array = [1,2,3]
var box = document.querySelector('.box')
box.addEventListener('click', eventFn)
點擊 box 時會顯示?
結果會是顯示三次的Ryder
,雖然eventFn
是由事件觸發,但this
所在的funciotn
是forEach
的 callback function ,而 callback function 也是屬於簡易呼叫的一種,因此this
指向的是 window ,所以顯示全域的Ryder
。
參考文獻
- JavaScript 核心篇 (六角學院)
- 或許我從一開始就沒有很懂 this
- 關於this是一個很神奇的東西這件事情