不論是學習或是開發時,我們容易被 this 的指向搞的頭昏眼花,接下來會花兩個篇幅介紹 this 指向。
也因為 this 容易把人搞亂這邊先列出影響 this 指向的方法:
本章節會先介紹上面 簡易呼叫、物件函式呼叫 ,addEventListener() 監聽事件觸發的函式這三個。
bind ﹑apply 、 call 綁定方法、嚴格模式 下篇則會說明這兩組狀況,剩餘的 new 建構式 、箭頭函式 則會到他們各自章節介紹。
先來看看指向全域的(window) 的 this 寫法:
var name = 'Ryder'
function showName() {
console.log(this.name)
}
showName()
這邊直接使用 showName()
這種直接呼叫函式的方法,這種直接呼叫函式的方法我們稱做 簡易呼叫 ,只要是由簡易呼叫觸發的函式,他當中的 this
一律指向 window
。
可以看到範例中確實會顯示 Ryder
,其實範例中 this
指向的是 window
。
順帶一題我們常用的 forEach()
、 filter()
中的 callback function ,他也是屬於 簡易呼叫 ,例如:
var 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
指向物件的寫法:
var name = 'Ryder';
var obj = {
name: 'Jack',
showName() {
console.log(this.name) // Jack
},
}
obj.showName()
這個範例中 this 指向的是 obj
這個物件本身,關於物件函式的 this
指向有一個小撇步, this 指向的位置就是,呼叫函式 xxx()
的上一層物件 ,如圖:
在使用另一個多層物件來看看結果是否一致
var name = 'Ryder';
var obj1 = {
name: 'Jack',
obj2: {
name: 'Alice',
showName() {
console.log(this.name) //Alice
},
}
}
obj1.obj2.showName()
在根據小撇步 this
會是 showName()
的上一層物件,圖片就會是
而答案也是正確的,範例中的 this
指向的會是 obj2
,因此這個範例顯示 'Alice'
function 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
的指向好像並不困難,上述都是建立在程式碼拆開來說的情況,接下來就以上面觀念,舉出一些容易讓人覺的混亂例子,並且在一一說明。
範例一:
var 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
是否被替換。
範例二:
var 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
範例三:
function 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 核心篇 (六角學院)