箭頭函式(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
查看全部
發表第一個留言支持創作者!
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
連續追了好幾部韓劇,滿腦子都是韓國烤肉,想約就約,cue了幾個韓式料理控的好朋友,今晚就一起到北安路上的豬頭妹韓式燒肉吃到飽,一圓我的韓式燒肉夢😍😍😍,加上現在物價漲漲漲,來吃到飽餐廳,很有賺回本的感覺~(Emma就交給老公啦~
Thumbnail
在 JavaScript ES6 之前,JavaScript 的函式主要是使用 function 關鍵字來定義的。而箭頭函式是 JavaScript ES6 中新增的功能,它提供了一種更簡潔的方式來定義函式。
Thumbnail
投資理財初學者應該遵循五個關鍵投資原則,包括了解風險承受度、分散投資、長期投資、控制成本和持續學習。這些原則將有助於建立成功的投資組合,降低風險,提高回報率,並不斷學習以獲得更好的投資成果。我是榮恩,讓我們一起實現財富自由吧 !
Thumbnail
心理變態是一種病?反社會人格是遺傳的?世界上竟然有反社會人格的腦神經科學家?
Thumbnail
你對於目前的投資策略或收益不滿意嗎?你覺得現在的通膨太高,想找一個有效的抗通膨方法嗎?其實透過穩健的存幣生息法,也能輕鬆獲得6-10%的年化收益!
注意自己在環境中 能不能夠放鬆 當個觀察者 一個環境讓觀察者過度活躍 對右箭頭身體有害 聽到Lydia這樣敘述C4右箭頭 回想過去一個月 我能觀察的時間非常的少 光是看著不動 夥伴會覺得 這樣不會知道結果 不用觀察那麼久 先做再說 讓我覺得很焦慮XDDDDD ~~~~~~~~~~~~~~~~~~
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
連續追了好幾部韓劇,滿腦子都是韓國烤肉,想約就約,cue了幾個韓式料理控的好朋友,今晚就一起到北安路上的豬頭妹韓式燒肉吃到飽,一圓我的韓式燒肉夢😍😍😍,加上現在物價漲漲漲,來吃到飽餐廳,很有賺回本的感覺~(Emma就交給老公啦~
Thumbnail
在 JavaScript ES6 之前,JavaScript 的函式主要是使用 function 關鍵字來定義的。而箭頭函式是 JavaScript ES6 中新增的功能,它提供了一種更簡潔的方式來定義函式。
Thumbnail
投資理財初學者應該遵循五個關鍵投資原則,包括了解風險承受度、分散投資、長期投資、控制成本和持續學習。這些原則將有助於建立成功的投資組合,降低風險,提高回報率,並不斷學習以獲得更好的投資成果。我是榮恩,讓我們一起實現財富自由吧 !
Thumbnail
心理變態是一種病?反社會人格是遺傳的?世界上竟然有反社會人格的腦神經科學家?
Thumbnail
你對於目前的投資策略或收益不滿意嗎?你覺得現在的通膨太高,想找一個有效的抗通膨方法嗎?其實透過穩健的存幣生息法,也能輕鬆獲得6-10%的年化收益!
注意自己在環境中 能不能夠放鬆 當個觀察者 一個環境讓觀察者過度活躍 對右箭頭身體有害 聽到Lydia這樣敘述C4右箭頭 回想過去一個月 我能觀察的時間非常的少 光是看著不動 夥伴會覺得 這樣不會知道結果 不用觀察那麼久 先做再說 讓我覺得很焦慮XDDDDD ~~~~~~~~~~~~~~~~~~