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 |
浏览器兼容性
加载中…