上回提到魔法學姊艾草(鳥)在練習英文。
艾草:「This, These, That, Those ...,可惡鳥嘴太難發音了吧,啾啾啾~啾啾啾!」
「少來,平常講話講得很順,這時候倒是怪起鳥嘴了!你幹嘛最近那麼認真再唸英文呀?」
艾草:「嘿嘿嘿,當然是為了唸魔法咒語的時候看起來更酷呀!啊哈哈哈哈。」
「...好唷,我來教你吧,做為交換,昨天教學的 this 可以多說一些嗎?什麼指向魔法那邊聽不太懂。」
艾草:「好耶!那現在就開始吧 ~~」

上一篇文章提到了一些常見 this 指向,這篇會提到取不到值的情境與解法。
透過上篇文章,可能會認為只要透過物件方法呼叫的函式,都會指向物件本身。但並不是只要透過此種方式呼叫就都會指向該物件,以 Callback Function setTimeout 來舉例:
var name = '全域艾草';
let obj = {
  name: '艾草',
  sayHello: function () {
		console.log(`Hello , ${this.name}`);//Hello , 艾草
    setTimeout(function () {
      console.log(`Hello , ${this.name}`);//Hello , 全域艾草
    }, 0);
  }
};
obj.sayHello();
可以看到透過 setTimeout 去 console.log 印出的值是全域艾草,代表 this 是指向到全域,多數Callback Function 運作方式偏向簡易呼叫,因此 this 會指向全域。
如何讓 setTimeout 指向到物件內呢?解決的其中一個方式就是 - 箭頭函式!
箭頭函式沒有自己的 this ,所以會與外層函式作用域的 this 指向相同,將上方案例修改為箭頭函式:
var name = '全域艾草';
let obj = {
  name: '艾草',
  sayHello: function () {
		console.log(`Hello , ${this.name}`);//Hello , 艾草
    setTimeout(()=> {
      console.log(`Hello , ${this.name}`);//Hello , 艾草
    }, 0);
  }
};
obj.sayHello();
透過箭頭函式沒有自己 this 的特性,this 會指向 obj,也可以成功取用到 obj 物件內的屬性值。
但如果不夠掌握箭頭函式的特性,也可能會遇到莫名的地方。
var name = '全域艾草';
let obj = {
  name: '艾草',
  sayHello: () => {
    console.log(`Hello , ${this.name}`);//Hello , 全域艾草
  }
};
obj.sayHello();
像這樣當看到呼叫方式 obj.sayHello() 時可能會預期 this 是指向前方的 obj ,但印出結果 this 的指向卻是指向全域。
因為箭頭函式沒有自己的 this 所以它會與外層作用域的 this 指向相同,但箭頭函式 sayHello 外層並沒有其他函式作用域可以參考,所以它會指向到全域。
當然也不只箭頭函式可以解決 Callback Function 指向全域的情況,接著來聊聊其他方法。
可以先於 sayHello() 設定一個自定義名稱變數,這邊以常見的命名方式 self 舉例,並將該 self 指向 sayHello() 的 this 。
將 setTimeout 內要取用 this 的地方,替換成使用存取外層作用域 this 的變數 self 。
var name = '全域艾草';
let obj = {
  name: '艾草',
  sayHello: function () {
    const self = this;
    setTimeout(function () {
      console.log(`Hello , ${self.name}`);//Hello , 艾草
    }, 0);
  }
};
obj.sayHello();
透過 bind() 並傳入參數為 this 想指向的地方,範例傳入obj 後,bind() 就會幫你綁定到 obj 上,因此可以順利取用到 obj 內的屬性值。
var name = '全域艾草';
let obj = {
  name: '艾草',
  sayHello: function () {
    setTimeout(function () {
      console.log(`Hello , ${this.name}`);//Hello , 艾草
    }.bind(obj), 0);
  }
};
obj.sayHello();
使用 bind() 綁定的其他例子
像這樣 bind 直接幫你把  this 綁定好並回傳綁好的函式給你,接下來不管你想怎麼呼叫它,它都不會變心,真的很棒!
var name = '全域艾草';
let obj = {
  name: '艾草',
};
function sayHello(){
 console.log(`Hello , ${this.name}`);//Hello , 全域艾草
}
sayHello();
let sayHello2 = sayHello.bind(obj);
sayHello2();//Hello , 艾草
最後的自我小吐槽:寫到這邊才發現這兩篇 this 文章程式碼都用艾草來舉例,到底誰會想一直跟自己打招呼啦,當初的自己到底在想啥呢...
setTimeout 會指向全域this
this 指向問題的方法
this 指向this 指向後,提供內層使用該變數bind() 綁定JavaScript 全攻略:克服 JS 的奇怪部分(Udemy)
Vue 3 實戰影音課程(六角學院)
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
https://realdennis.medium.com/javascript-聊聊call-apply-bind的差異與相似之處-2f82a4b4dd66