(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
    5
    var 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
    12
    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 指向物件的寫法:
    1
    2
    3
    4
    5
    6
    7
    8
    var 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
    11
    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() 監聽事件觸發的函式

    1
    2
    3
    4
    5
    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 的指向好像並不困難,上述都是建立在程式碼拆開來說的情況,接下來就以上面觀念,舉出一些容易讓人覺的混亂例子,並且在一一說明。

    範例一:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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 是否被替換。

    範例二:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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


    範例三:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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

    參考文獻

  • JavaScript 核心篇 (六角學院)
  • 或許我從一開始就沒有很懂 this
  • 關於this是一個很神奇的東西這件事情