箭頭函式(Arrow function)的 this 究竟是什麼?如何簡單理解 Lexical "this"?

閱讀時間約 8 分鐘
Photo by Juanjo Jaramillo on Unsplash
在傳統開發的過程中,很容易會搞混一般的 this 和箭頭函式(arrow function)中的 lexcial "this" 兩者的差異。在傳統函式中,this 是在函式調用時動態決定,換句話說只要是哪一個物件呼叫函式,他就會抓取該物件進行呼叫;而箭頭函式則是在定義時就決定,並繼承自父層的作用域。

傳統的 this 定義

我們直接以下列的案例來說明。我們實例化一個物件 person,並設定一個 greet 的方法,其中使用 SetTimeout 的方法來執行。
const person = {
  name: 'Chris',
  greet: function() {
    setTimeout(function() {
      console.log(`Hello, my name is ${this.name}`);
    }, 1000);
  }
};
person.greet(); // Hello, my name is undefined (after 1 second)
在上面的這個例子中,因為 setTimeout 並非 JavaScript 內建的函式,而是屬於 Web API,因此在執行 SetTimeout 後會先將 Timer 放進 Web API 等待呼叫。
這時就會先在 Web API 確定是否一秒後,確認一秒後會執行 Web API 的 Timeout,將 Timeout 放進 task Quene 移到 Stack 中執行。此時在全域環境(Stack)確認後,並沒有待執行的內容。
接著 Timeout 函式就會被放進 Stack 中,Timeout 就會呼叫 Callback 來執行。此時,執行的環境就會是 window 而非此處的 person。因此結果就會是 "Hello, my name is undefined"。
除非特別使用 Call 或 Apply 來綁定呼叫的物件,才能讓這個 greet 順利運行。以下是實際範例:
const person = {
name: 'Chris',
greet: function() {
setTimeout(function() {
console.log(`Hello, my name is ${this.name}`);
}.apply(this), 1000);
}
};
person.greet(); // Hello, my name is Chris (after 1 second)
透過 Apply 強制將此處的 this(就是 person)綁定給 setTimeout 的 callback function,才在執行 greet 時,順利顯示正確的 Chris。

箭頭函式中的 lexcial this 定義

當初在理解父層 this 的環境定義時,時常不確定指的到底是哪一個環境。但這裡只要簡單的記得:
箭頭函式的 this 指的是定義該 function 的環境(lexical Scope)中,所使用的 this。
我們一樣以上面的例子說明:
const person = {
name: 'Chris',
greet: function() {
setTimeout(() => {
console.log(`Hello, my name is ${this.name}`);
}, 1000);
}
};

person.greet(); // Hello, my name is Chris
雖然上述的 setTimeout 創建了一個執行環境(Execution context),但因為箭頭函式的 this 是 lexical bound。
可以看到此處創建箭頭函式的環境,是被宣告在 greet 這個 function 裡,所以 greet 就是這個箭頭函式的 Lexical Scope。因此此處 greet 的 this,就是箭頭函式的 this。因此,此處仍然會顯示 Chris。
這裡也舉另一個例子,如果 greet 不要使用一個 function 包裹,那上層的 greet 裡使用 this 一樣會抓到 window。因為在定義的當下,再往上一層的 Scope Chain 會抓到 window,因此此處的 this 就會是 window。
因此若要在方法使用箭頭函式,則需使用傳統函式包裹,才能順利使用。

如何理解執行上下文(Execution Context)?

一般來說,總共有五中 Execution Context 會生成:
  1. 全域執行上下文(Global Execution Context):全域執行上下文是在程式開始執行時創建的,代表整個程式的執行環境。在全域執行上下文中,會創建全域物件(global object)和全域範疇(global scope)。
  2. 函式執行上下文(Function Execution Context):每當函式被呼叫時,都會創建一個新的函式執行上下文。每個函式執行上下文都有自己的範疇,包含函式內部的變數、參數、函式內部定義的其他函式等。
  3. Eval 函式執行上下文(Eval Function Execution Context):當程式碼被 eval() 函式執行時,會創建一個 Eval 函式執行上下文。Eval 函式執行上下文允許執行字串中的程式碼,但在現代 JavaScript 中,建議盡量避免使用 eval() 函式。
  4. 模組執行上下文(Module Execution Context):在使用 ES6 模組的情況下,每個模組都會有自己的模組執行上下文,模組內的變數、函式等僅在模組內可見,預設不會暴露到全域範疇。
  5. 物件執行上下文(Object Execution Context):當函式作為物件的方法被呼叫時,該函式執行上下文被稱為物件執行上下文。在物件執行上下文中,物件本身被設置為 this,並且可以訪問該物件的屬性和方法。
在理解箭頭函式的 this 時,最容易被搞混的原因,其實是來自於上述的「物件執行上下文」與「函式執行上下文」,兩者的認識不夠清楚。

箭頭函式的 this 如何參考?

一般來說,當一個傳統函式作為某物件的執行方法時,就會創建物件執行上下文。因此,在方法中使用 this,就會指向該物件本身。
然而,將箭頭函式使用在物件方法時,因為本身箭頭函式不會建立物件執行上下文,因為他本身並沒有 this。相反地,箭頭函式僅參考外層範疇(lexical Scope)環境中的 this、argument。除此之外,箭頭函式也沒有自己的建構函式。
換句話說,如果直接在全域中物件呼叫 {key: this},該 this 會指向全域物件,因為此處的 this 仍然屬於全域執行上下文,因此 this 依然會顯示 window,或是嚴格模式下的 undefined。
因此,只要簡單瞭解:
箭頭函式本身並不會建立物件上下文,而是僅會參考外層環境(Lexical Scope)的 this,建立函式上下文而已。

瞭解 this 在 JavaScript 中扮演的角色

原先 this 綁定執行的物件,是為了呼叫時的方便;但隨著開發的複雜、物件繼承的情境,甚至非同步的問題變得複雜,也因此才出現了箭頭函式。
但箭頭函式並非要取代傳統函式,因為箭頭函式本身並沒有自己的 this 可以使用,單純是綁定外層的父環境,因此在 JavaScript 後續開發的過程,主要扮演了互補的角色。

參考資料

即將進入廣告,捲動後可繼續閱讀
為什麼會看到廣告
此處作為整理前端(Frontend)和相關的 HTML、CSS、JavaScript、React 等前端觀念與技巧,全部都會收錄在這個專題之中。同時也會將相關的技術與反思記錄在此,歡迎各位讀者互相交流。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
連續追了好幾部韓劇,滿腦子都是韓國烤肉,想約就約,cue了幾個韓式料理控的好朋友,今晚就一起到北安路上的豬頭妹韓式燒肉吃到飽,一圓我的韓式燒肉夢😍😍😍,加上現在物價漲漲漲,來吃到飽餐廳,很有賺回本的感覺~(Emma就交給老公啦~
Thumbnail
在 JavaScript ES6 之前,JavaScript 的函式主要是使用 function 關鍵字來定義的。而箭頭函式是 JavaScript ES6 中新增的功能,它提供了一種更簡潔的方式來定義函式。
Thumbnail
投資理財初學者應該遵循五個關鍵投資原則,包括了解風險承受度、分散投資、長期投資、控制成本和持續學習。這些原則將有助於建立成功的投資組合,降低風險,提高回報率,並不斷學習以獲得更好的投資成果。我是榮恩,讓我們一起實現財富自由吧 !
Thumbnail
心理變態是一種病?反社會人格是遺傳的?世界上竟然有反社會人格的腦神經科學家?
Thumbnail
你對於目前的投資策略或收益不滿意嗎?你覺得現在的通膨太高,想找一個有效的抗通膨方法嗎?其實透過穩健的存幣生息法,也能輕鬆獲得6-10%的年化收益!
注意自己在環境中 能不能夠放鬆 當個觀察者 一個環境讓觀察者過度活躍 對右箭頭身體有害 聽到Lydia這樣敘述C4右箭頭 回想過去一個月 我能觀察的時間非常的少 光是看著不動 夥伴會覺得 這樣不會知道結果 不用觀察那麼久 先做再說 讓我覺得很焦慮XDDDDD ~~~~~~~~~~~~~~~~~~
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
連續追了好幾部韓劇,滿腦子都是韓國烤肉,想約就約,cue了幾個韓式料理控的好朋友,今晚就一起到北安路上的豬頭妹韓式燒肉吃到飽,一圓我的韓式燒肉夢😍😍😍,加上現在物價漲漲漲,來吃到飽餐廳,很有賺回本的感覺~(Emma就交給老公啦~
Thumbnail
在 JavaScript ES6 之前,JavaScript 的函式主要是使用 function 關鍵字來定義的。而箭頭函式是 JavaScript ES6 中新增的功能,它提供了一種更簡潔的方式來定義函式。
Thumbnail
投資理財初學者應該遵循五個關鍵投資原則,包括了解風險承受度、分散投資、長期投資、控制成本和持續學習。這些原則將有助於建立成功的投資組合,降低風險,提高回報率,並不斷學習以獲得更好的投資成果。我是榮恩,讓我們一起實現財富自由吧 !
Thumbnail
心理變態是一種病?反社會人格是遺傳的?世界上竟然有反社會人格的腦神經科學家?
Thumbnail
你對於目前的投資策略或收益不滿意嗎?你覺得現在的通膨太高,想找一個有效的抗通膨方法嗎?其實透過穩健的存幣生息法,也能輕鬆獲得6-10%的年化收益!
注意自己在環境中 能不能夠放鬆 當個觀察者 一個環境讓觀察者過度活躍 對右箭頭身體有害 聽到Lydia這樣敘述C4右箭頭 回想過去一個月 我能觀察的時間非常的少 光是看著不動 夥伴會覺得 這樣不會知道結果 不用觀察那麼久 先做再說 讓我覺得很焦慮XDDDDD ~~~~~~~~~~~~~~~~~~