Skip to content

2026-05-22

/ 11 分鐘閱讀

/ JavaScript Hard Parts v3

方法內的巢狀函式與箭頭函式的 this 綁定

本章節建議從 物件導向 JavaScript:OOP 的核心目標與原型鏈基礎 開始閱讀

問題:方法內部呼叫普通函式時,this 會跑掉

承接前一節,當我們在 increment 方法內再定義並呼叫一個普通函式 add1 時,問題就出現了:

javascript
const userFunctionStore = {
    increment: function () {
        function add1() {
            this.score++;
        }
        add1();
    },
};

執行 user2.increment() 時:

  1. 進入 increment 的執行環境,this 被自動設為 user2(點左側的物件)。
  2. 宣告 add1 函式並立刻呼叫它。
  3. 進入 add1 的執行環境,此時 add1 不是以 物件.方法() 的形式被呼叫,沒有點的左側可以參照,因此 this 預設指向全域物件 window
  4. this.score++ 變成 window.score++,試圖對一個不存在的全域屬性加一,完全不是我們想要的結果。

舊的解法:that = this

在箭頭函式出現之前,開發者的慣用解法是在呼叫子函式之前,先把 this 的值存進一個普通變數:

javascript
increment: function() {
  const that = this
  function add1() { that.score++ }
  add1()
}

that 透過閉包保留了 increment 執行時的 this 值(即 user2),子函式內改用 that 存取即可。這個做法雖然有效,但被廣泛認為是一個難看的 hack。

正確(現在的)解法:箭頭函式的 this

箭頭函式解決了這個問題。它的 this 不由呼叫方式決定,而是由定義時所在的執行環境決定,也就是詞法作用域(lexical scope)的邏輯:

javascript
const userFunctionStore = {
    increment: function () {
        const add1 = () => {
            this.score++;
        };
        add1();
    },
};

add1 以箭頭函式定義時,它的 this 在定義的瞬間就被綁定為當下的 this,也就是 increment 被呼叫時的 this:即 user2。之後不管 add1 以什麼形式被呼叫,this 永遠是 user2

函式類型this 的決定方式
普通函式(function由呼叫時點左側的物件決定;若無點,則為 window
箭頭函式(=>由定義時所在執行環境的 this 決定(詞法綁定)

需要注意的是,箭頭函式適合作為方法內部的子函式,但不適合作為方法本身。若把 increment 改成箭頭函式,它就無法在呼叫時正確捕捉點左側的物件作為 this,反而會把定義時的 this 固定下來。

解法二的總結

使用原型鏈的解法二在技術上是完整且有效的:

優點:高效能,所有使用者物件共用一份方法;完全能達成封裝與可推理的目標。

缺點:每次建立物件都需要手動寫 Object.create()、逐一加入屬性、最後 return,雖然只是幾個字,卻不夠直覺,也不是 JavaScript 的慣用寫法。

這個重複的樣板正是下一個解法要消除的,也就是 new 關鍵字自動化了這整個過程。

複習

當一個函式以物件方法的形式被呼叫時,會自動建立的隱含參數是什麼?

隱含參數是 this,它會被自動設為呼叫該方法時點(.)左側的物件。

當一個普通函式被定義並在方法內部呼叫(非箭頭函式語法)時,該內部函式中的 this 會指向什麼?

內部函式中的 this 會指向全域物件(在瀏覽器中為 window),而不是呼叫外層方法的那個物件。

箭頭函式中的 this 與普通函式有何不同?

箭頭函式的 this 採詞法綁定,設為箭頭函式被定義時所在執行環境的 this 值,而不是由函式的呼叫方式來決定。

在以下程式碼中,add1 函式內的 this 會指向什麼?

javascript
increment: function() {
  const add1 = () => {
    this.score++;
  };
  add1();
}

this 會指向呼叫 increment 的那個物件,因為箭頭函式會保留其定義時所在執行環境的 this 值。

小測驗

箭頭函式執行時,如何決定 `this` 的指向? 使用箭頭函式被定義時所在執行環境的 `this` 值
在箭頭函式出現之前,開發者用什麼方式來保留巢狀函式中正確的 `this` 指向? 在巢狀函式之前宣告 `that = this`,再於子函式中使用 that
使用點記法呼叫物件上的方法(例如 user2.increment())時,會發生什麼事? 建立一個新的執行環境,並將 `this` 設為點左側的物件

此文章是 FrontendMasters 上的 The Hard Parts of JavaScript 課程筆記

最後更新時間:

Buy Me A Coffee

系列章節 第 43 篇 / 共 46 篇

0 %
MIT Licensed | Copyright © 2025-present Wen-Hsiu's Blog
Photo by Federica Galli on Unsplash