arguments.callee
已棄用:此特性不再推薦。雖然某些瀏覽器可能仍然支援它,但它可能已經從相關的網路標準中刪除,可能正在刪除過程中,或者可能僅為相容性目的而保留。請避免使用它,如果可能,請更新現有程式碼;請參閱本頁底部的相容性表格以指導您的決策。請注意,此特性可能隨時停止工作。
arguments.callee資料屬性包含當前正在執行的函式,該函式擁有這些引數。
值
對當前正在執行的函式的引用。
arguments.callee的屬性特性 | |
|---|---|
| 可寫 | 是 |
| 可列舉 | 否 |
| 可配置 | 是 |
描述
callee是arguments物件的一個屬性。它可以在函式體內部用來引用當前正在執行的函式。當函式的名稱未知時,例如在沒有名稱的函式表示式(也稱為“匿名函式”)中,這非常有用。
(以下文字主要改編自olliej在Stack Overflow上的一個回答)
早期版本的JavaScript不允許命名函式表示式,因此無法建立遞迴函式表示式。
例如,這種語法有效
function factorial(n) {
return n <= 1 ? 1 : factorial(n - 1) * n;
}
[1, 2, 3, 4, 5].map(factorial);
但
[1, 2, 3, 4, 5].map(function (n) {
return n <= 1 ? 1 : /* what goes here? */ (n - 1) * n;
});
無效。為了解決這個問題,添加了arguments.callee,這樣你就可以這樣做
[1, 2, 3, 4, 5].map(function (n) {
return n <= 1 ? 1 : arguments.callee(n - 1) * n;
});
然而,arguments.callee的設計存在多個問題。第一個問題是遞迴呼叫將獲得不同的this值。例如
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透過允許命名函式表示式解決了這些問題。例如
[1, 2, 3, 4, 5].map(function factorial(n) {
return n <= 1 ? 1 : factorial(n - 1) * n;
});
這有很多好處
- 該函式可以像其他函式一樣在你的程式碼內部被呼叫
- 它不會在外層作用域中建立變數(IE 8及以下版本除外)
- 它比訪問arguments物件有更好的效能
嚴格模式已經禁止了其他洩漏堆疊資訊的屬性,例如函式的caller屬性。這是因為檢視呼叫堆疊有一個主要影響:它使得大量最佳化變得不可能,或變得更加困難。例如,如果你不能保證函式f不會呼叫未知函式,就不可能內聯f。
function f(a, b, c, d, e) {
return a ? b * c : d * e;
}
如果JavaScript直譯器不能保證在呼叫時所有提供的引數都是數字,它需要要麼在內聯程式碼之前插入對所有引數的檢查,要麼無法內聯該函式。這意味著任何可能輕易內聯的呼叫站點都會積累大量的守衛。在這種特定情況下,一個智慧的直譯器應該能夠重新安排檢查以使其更最佳化,並且不檢查任何不會被使用的值。然而,在許多情況下,這根本不可能,因此也無法進行內聯。
示例
在匿名遞迴函式中使用arguments.callee
遞迴函式必須能夠引用自身。通常,函式透過其名稱引用自身。然而,匿名函式(可以透過函式表示式或Function建構函式建立)沒有名稱。因此,如果沒有可訪問的變數引用它,函式引用自身的唯一方法是arguments.callee。
以下示例定義了一個函式,該函式又定義並返回一個階乘函式。此示例並不實用,幾乎沒有不能透過命名函式表示式實現相同結果的情況。
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組合子。這裡我們不解釋它如何工作,只解釋它確實有效。
// 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 |
瀏覽器相容性
載入中…