arguments.callee

已棄用:此特性不再推薦。雖然某些瀏覽器可能仍然支援它,但它可能已經從相關的網路標準中刪除,可能正在刪除過程中,或者可能僅為相容性目的而保留。請避免使用它,如果可能,請更新現有程式碼;請參閱本頁底部的相容性表格以指導您的決策。請注意,此特性可能隨時停止工作。

注意:嚴格模式下訪問arguments.callee會丟擲TypeError。如果函式必須引用自身,則給函式表示式一個名稱或使用函式宣告

arguments.callee資料屬性包含當前正在執行的函式,該函式擁有這些引數。

對當前正在執行的函式的引用。

arguments.callee的屬性特性
可寫
可列舉
可配置

注意:在具有簡單引數的非嚴格函式中,callee才是一個數據屬性(在這種情況下,arguments物件也是自動同步的)。否則,它是一個訪問器屬性,其getter和setter都會丟擲TypeError

描述

calleearguments物件的一個屬性。它可以在函式體內部用來引用當前正在執行的函式。當函式的名稱未知時,例如在沒有名稱的函式表示式(也稱為“匿名函式”)中,這非常有用。

(以下文字主要改編自olliej在Stack Overflow上的一個回答

早期版本的JavaScript不允許命名函式表示式,因此無法建立遞迴函式表示式。

例如,這種語法有效

js
function factorial(n) {
  return n <= 1 ? 1 : factorial(n - 1) * n;
}

[1, 2, 3, 4, 5].map(factorial);

js
[1, 2, 3, 4, 5].map(function (n) {
  return n <= 1 ? 1 : /* what goes here? */ (n - 1) * n;
});

無效。為了解決這個問題,添加了arguments.callee,這樣你就可以這樣做

js
[1, 2, 3, 4, 5].map(function (n) {
  return n <= 1 ? 1 : arguments.callee(n - 1) * n;
});

然而,arguments.callee的設計存在多個問題。第一個問題是遞迴呼叫將獲得不同的this值。例如

js
function sillyFunction(recursed) {
  if (this !== globalThis) {
    console.log("This is:", this);
  } else {
    console.log("This is the global");
  }

  if (!recursed) {
    return arguments.callee(true);
  }
}

sillyFunction();
// This is the global
// This is: [object Arguments]

此外,對arguments.callee的引用使得在一般情況下無法進行內聯和尾遞迴。(你可以在特定情況下透過跟蹤等方式實現,但由於需要進行不必要的檢查,即使是最好的程式碼也仍不理想。)

ECMAScript 3透過允許命名函式表示式解決了這些問題。例如

js
[1, 2, 3, 4, 5].map(function factorial(n) {
  return n <= 1 ? 1 : factorial(n - 1) * n;
});

這有很多好處

  • 該函式可以像其他函式一樣在你的程式碼內部被呼叫
  • 它不會在外層作用域中建立變數(IE 8及以下版本除外
  • 它比訪問arguments物件有更好的效能

嚴格模式已經禁止了其他洩漏堆疊資訊的屬性,例如函式的caller屬性。這是因為檢視呼叫堆疊有一個主要影響:它使得大量最佳化變得不可能,或變得更加困難。例如,如果你不能保證函式f不會呼叫未知函式,就不可能內聯f

js
function f(a, b, c, d, e) {
  return a ? b * c : d * e;
}

如果JavaScript直譯器不能保證在呼叫時所有提供的引數都是數字,它需要要麼在內聯程式碼之前插入對所有引數的檢查,要麼無法內聯該函式。這意味著任何可能輕易內聯的呼叫站點都會積累大量的守衛。在這種特定情況下,一個智慧的直譯器應該能夠重新安排檢查以使其更最佳化,並且不檢查任何不會被使用的值。然而,在許多情況下,這根本不可能,因此也無法進行內聯。

示例

在匿名遞迴函式中使用arguments.callee

遞迴函式必須能夠引用自身。通常,函式透過其名稱引用自身。然而,匿名函式(可以透過函式表示式Function建構函式建立)沒有名稱。因此,如果沒有可訪問的變數引用它,函式引用自身的唯一方法是arguments.callee

以下示例定義了一個函式,該函式又定義並返回一個階乘函式。此示例並不實用,幾乎沒有不能透過命名函式表示式實現相同結果的情況。

js
function create() {
  return function (n) {
    if (n <= 1) {
      return 1;
    }
    return n * arguments.callee(n - 1);
  };
}

const result = create()(5); // returns 120 (5 * 4 * 3 * 2 * 1)

使用Y組合子進行匿名函式遞迴

儘管函式表示式現在可以命名,但箭頭函式始終保持匿名,這意味著它們在未首先賦值給變數的情況下無法引用自身。幸運的是,在Lambda演算中有一個很好的解決方案,它允許函式既是匿名的又是自引用的。這種技術被稱為Y組合子。這裡我們不解釋它如何工作,只解釋它確實有效。

js
// The Y-combinator: a utility function!
const Y = (hof) => ((x) => x(x))((x) => hof((y) => x(x)(y)));

console.log(
  [1, 2, 3, 4, 5].map(
    // Wrap the higher-order function in the Y-combinator
    // "factorial" is not a function's name: it's introduced as a parameter
    Y((factorial) => (n) => (n <= 1 ? 1 : factorial(n - 1) * n)),
  ),
);
// [ 1, 2, 6, 24, 120 ]

注意:這種方法為每次迭代分配一個新的閉包,這可能會顯著增加記憶體使用。這裡只是為了演示可能性,但在生產中應避免使用。請改用臨時變數或命名函式表示式。

規範

規範
ECMAScript® 2026 語言規範
# sec-arguments-exotic-objects

瀏覽器相容性

另見