iT邦幫忙

0

JS This 指向誰

this 決定在於函式如何被呼叫

Why use This

  • 可用不同物件,調用同函式
  • 使 API 更簡潔且易於復用
function identify() { console.log(this.name) }
let me = { name: "Tom" }
let you = { name: "Mark" }
identify.call(me) // Tom
identify.call(you) // Mark

影響 this 的情境

默認綁定 => 指向 global 物件

  • 純粹的調用、直接呼叫Function
  • 嚴格模式("use strict") => 指向 undefined
  // 瀏覽器的 global物件 window
  console.log(this) // window
  
  // 瀏覽器的 global物件 window 
  function test(){ console.log(this) }
  test()

隱式綁定 => 指向 上下文 物件

  • 物件 => 指向 物件
  • 隱式綁定丟失: 函式調用的時候,無上下文,只是對函式的引用
  const obj = {
    test(){ console.log(this) }
  }

  const bar = obj.test
  // 等同於此 const bar = test(){ console.log(this) }
  bar() // window

new 綁定 ( new建構式 )

new 關鍵字會作

  • 建立新物件
  • 連接 Prototype
  • this 綁定至該新物件 (相當於物件綁定)
  const name = '全域'
  const callMethod = function () {
    this.name = 'Mark'
    console.log(this.name)
  }
  
  const myName = new callMethod()
  console.log(myName.name) // Mark

DOM 物件

  • this 指向 掛載事件的元件 e.currentTarget
  const parent = document.querySelector('.parent')
  parent.addEventListener('click', test)
  function test(e) {
  
    // e.target 指向觸發事件的元件  .child
    console.log('e.target',e.target);
    
    // e.currentTarget 指向掛載事件的元件 .parent
    console.log('e.currentTarget',e.currentTarget);
    
    // this 指向掛載事件的元件  .parent 即 e.currentTarget
    console.log('this',this);
  }

顯式綁定 => 強制綁向目標

  • bind / apply / call => 指向 目標

箭頭函式

  • 箭頭函式 => 指向 上一個非箭頭函式的 this
    因箭頭函式不會產生this,而會照 Scope Chain 向上找
  • 箭頭函式不可當建構式

閉包的this

  • 純粹的調用 => window
  const name = '全域';
  function callMethod() {
    const name = '區域'
    console.log(this.name) // '全域'
    return function () {  // 閉包
      const name = '區域的內層變數'
      console.log(this.name) // '全域'
    }
  }
  // 分兩段 callMethod() 回傳內部函式
  // 內部函式() 執行,其環境在 window 
  callMethod()()

Object & ArrowFunction

  • 縮寫與否 效果一樣
  • ArrowFunction 指向上一個非箭頭函式的This
  const name = '全域'
  const object = {
    name: 'Object 區域',
    callMethod: function () {
      const name = '區域'
      console.log(this.name) // 區域 (指向物件)
    },
    abbCallMethod () {
      const name = '縮寫函式區域'
      console.log(this.name) // 區域 (指向物件)
    },
    arrowCallMethod: () => {
      const name = '箭頭函式區域'
      console.log(this.name) // 全域
    },
  }
  object.callMethod()
  object.abbCallMethod()
  
  // 指向上一個非箭頭函式的This 此時為 window
  object.arrowCallMethod() 

物件內的閉包

  const name = '全域'
  const object = {
    name: 'Object 區域',
    callMethod: function () {
      const name = '區域'
      return function () {
        console.log(this.name) // '全域'
      }
    },
    callMethod2: function () {
      const name = '區域'
      const that = this
      return function () {
        console.log(that.name) // '區域'
      }
    }
  }

  // 分解成 object.callMethod() 得到 Function
  // 該 Function 則在 window 的環境下執行
  object.callMethod()()

  // that 指向 object 並保存在閉包中
  // 執行內部函式,找不到本層that向上找至 callMethod2中的that
  object.callMethod2()()

題目練習

const obj = {
  a(){
    function b(){ console.log(this); }
    b();
  }
}
obj.a(); // window (執行環境為 window)
const obj = {
  a(){ console.log(this) }
}

// 物件調用該Function執行
obj.a() // obj

const b = obj.a
b() // 直接執行、window
function a(){ console.log(this) }
const obj = {}
obj.a = a
obj.a() // obj
const obj = {
  a() {
    console.log(this) // obj
    function b() { 
      console.log(this) // window
    }
    b() // 直接執行的
  }
}
obj.a()
function a(){
  console.log(this) // e.currentTarget
}
const obj = {a}
btn.addEventListener('click', obj.a)
function a(){
  console.log(this) // window
}
const obj = {
  b(){
    // a 函式屬於直接執行
    return function (){ a() }
  }
}
btn.addEventListener('click', obj.b())
const obj = {
  b(){
    // e.currentTarget
    return function (){ console.log(this) }
  }
}
// obj.b() 等同於 console.log(this)
btn.addEventListener('click', obj.b())
// e.currentTarget
function a() { console.log(this) }
const obj  = { b(){ return a } }
btn.addEventListener('click', obj.b())

箭頭函式題目

const a  = () => {
  // this會去找外層的this
  console.log(this) // window
}
btn.addEventListener('click', a)
const obj = {
  a: () => {
    // window
    console.log(this)
  }
}
obj.a()
function a() {
  const b = () => {
    console.log(this) // window
  }
  b() // 直接執行
}

btn.addEventListener('click', a)

此題重要

function a() {
  // 此this作為 DOM事件監聽因此為 e.currentTarget
  console.log(this) 
  const b = () => {
    // 箭頭函式不產this 指向外層 e.currentTarget
    console.log(this) 
  }
  b() // 直接執行
}
btn.addEventListener('click', a)

參考資料

六角 This
OBKoro1's Blog
Kanboo
直播學程式


尚未有邦友留言

立即登入留言