iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
自我挑戰組

JavaScript 奇奇怪怪的核心觀念系列 第 16

(Day-16) this 介紹上 - this 的指向

前言

不論是學習或是開發時,我們容易被 this 的指向搞的頭昏眼花,接下來會花兩個篇幅介紹 this 指向。

也因為 this 容易把人搞亂這邊先列出影響 this 指向的方法:

  • 簡易呼叫 ( simple call)
  • 物件函式呼叫
  • addEventListener() 監聽事件觸發的函式
  • bind ﹑apply 、 call 綁定方法
  • 嚴格模式
  • new 建構式
  • 箭頭函式

本章節會先介紹上面 簡易呼叫、物件函式呼叫 ,addEventListener() 監聽事件觸發的函式這三個。

bind ﹑apply 、 call 綁定方法、嚴格模式 下篇則會說明這兩組狀況,剩餘的 new 建構式 、箭頭函式 則會到他們各自章節介紹。

簡易呼叫 ( simple call)

先來看看指向全域的(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'

addEventListener() 監聽事件觸發的函式

function eventFn(){
  console.log(this) // DOM
}
const box = document.querySelector('.box')
box.addEventListener('click', eventFn)

當我們使用 addEventListener 配合 clickmouseover 等等事件觸發的函式,他裡面的 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 所在的 funciotnforEach 的 callback function ,而 callback function 也是屬於簡易呼叫的一種,因此 this 指向的是 window ,所以顯示全域的 Ryder

參考文獻


上一篇
(Day15) 閉包進階使用,工廠模式及私有方法。
下一篇
(Day17) this 介紹下 - 綁定 this 的 call & apply & bind 與嚴格模式
系列文
JavaScript 奇奇怪怪的核心觀念30

尚未有邦友留言

立即登入留言