arguments.callee

Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.

注意:在 严格模式 中访问 arguments.callee 将抛出 TypeError。如果函数必须引用自身,请为 函数表达式 指定名称或使用 函数声明

¥Note: Accessing arguments.callee in strict mode will throw a TypeError. If a function must reference itself, either give the function expression a name or use a function declaration.

arguments.callee 数据属性包含参数所属的当前正在执行的函数。

¥The arguments.callee data property contains the currently executing function that the arguments belong to.

¥Value

对当前正在执行的函数的引用。

¥A reference to the currently executing function.

Property attributes of arguments.callee
Writable yes
Enumerable no
Configurable yes

注意:callee 是仅在具有简单参数的非严格函数中的数据属性(在这种情况下,arguments 对象也是 auto-syncing)。否则,它是一个访问器属性,其 getter 和 setter 都抛出 TypeError

¥Note: callee is a data property only in non-strict functions with simple parameters (in which case the arguments object is also auto-syncing). Otherwise, it is an accessor property whose getter and setter both throw a TypeError.

描述

¥Description

calleearguments 对象的属性。它可用于引用该函数的函数体内当前正在执行的函数。当函数名称未知时(例如在没有名称的函数表达式中(也称为 "匿名函数")),这非常有用。

¥callee is a property of the arguments object. It can be used to refer to the currently executing function inside the function body of that function. This is useful when the name of the function is unknown, such as within a function expression with no name (also called "anonymous functions").

(以下文字大部分改编自 olliej 在 Stack Overflow 上的回答

¥(The text below is largely adapted from a Stack Overflow answer by olliej)

JavaScript 的早期版本不允许命名函数表达式,因此你无法创建递归函数表达式。

¥Early versions of JavaScript did not allow named function expressions, and for this reason you could not make a recursive function expression.

例如,以下语法有效:

¥For example, this syntax worked:

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

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

但:

¥but:

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

没有。为了解决这个问题,添加了 arguments.callee,这样你就可以这样做

¥did not. To get around this arguments.callee was added so you could do

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

然而,arguments.callee 的设计存在多个问题。第一个问题是递归调用会得到不同的 this 值。例如:

¥However, the design of arguments.callee has multiple issues. The first problem is that the recursive call will get a different this value. For example:

js
const global = this;

const sillyFunction = function (recursed) {
  if (this !== global) {
    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 的引用使得内联和尾递归在一般情况下变得不可能。(你可以在某些情况下通过跟踪等来实现它,但即使是最好的代码也不是最佳的,因为否则不需要进行检查。)

¥In addition, references to arguments.callee make inlining and tail recursion impossible in the general case. (You can achieve it in select cases through tracing, etc., but even the best code is suboptimal due to checks that would not otherwise be necessary.)

ECMAScript 3 通过允许命名函数表达式解决了这些问题。例如:

¥ECMAScript 3 resolved these issues by allowing named function expressions. For example:

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

这有很多好处:

¥This has numerous benefits:

  • 该函数可以像代码中的任何其他函数一样调用
  • 它不会在外部作用域中创建变量(IE 8 及以下版本除外
  • 它比访问参数对象具有更好的性能

严格模式禁止了其他泄漏堆栈信息的属性,例如函数的 caller 属性。这是因为查看调用堆栈有一个主要影响:它使得大量优化变得不可能,或者变得更加困难。例如,如果不能保证函数 f 不会调用未知函数,则无法内联 f

¥Strict mode has banned other properties that leak stack information, like the caller property of functions. This is because looking at the call stack has one single major effect: it makes a large number of optimizations impossible, or much more difficult. For example, if you cannot guarantee that a function f will not call an unknown function, it is not possible to inline f.

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

如果 JavaScript 解释器无法保证在调用时所有提供的参数都是数字,则它需要在内联代码之前插入对所有参数的检查,否则无法内联该函数。这意味着任何可能微不足道的内联调用站点都会积累大量的守卫。现在,在这种特殊情况下,智能解释器应该能够重新安排检查以使其更加优化,并且不检查任何不会使用的值。然而,在许多情况下这是不可能的,因此内联变得不可能。

¥If the JavaScript interpreter cannot guarantee that all the provided arguments are numbers at the point that the call is made, it needs to either insert checks for all the arguments before the inlined code, or it cannot inline the function. This means any call site that may have been trivially inlinable accumulates a large number of guards. Now in this particular case a smart interpreter should be able to rearrange the checks to be more optimal and not check any values that would not be used. However in many cases that's just not possible and therefore it becomes impossible to inline.

示例

¥Examples

在匿名递归函数中使用 arguments.callee

¥Using arguments.callee in an anonymous recursive function

递归函数必须能够引用自身。通常,函数通过其名称引用自身。但是,匿名函数(可以由 函数表达式Function 构造函数 创建)没有名称。因此,如果没有可访问的变量引用它,则函数引用自身的唯一方法是通过 arguments.callee

¥A recursive function must be able to refer to itself. Typically, a function refers to itself by its name. However, an anonymous function (which can be created by a function expression or the Function constructor) does not have a name. Therefore if there is no accessible variable referring to it, the only way the function can refer to itself is by arguments.callee.

以下示例定义一个函数,该函数又定义并返回一个阶乘函数。这个例子不太实用,而且几乎在任何情况下都无法用 命名函数表达式 实现相同的结果。

¥The following example defines a function, which, in turn, defines and returns a factorial function. This example isn't very practical, and there are nearly no cases where the same result cannot be achieved with named function expressions.

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 组合器的匿名函数的递归

¥Recursion of anonymous functions with a Y-combinator

尽管函数表达式现在可以命名,但 箭头函数 始终保持匿名,这意味着它们在未先分配给变量的情况下无法引用自身。幸运的是,在 Lambda 演算中有一个非常好的解决方案,它允许函数既是匿名的又是自引用的。该技术称为 Y 组合器。这里我们不会解释它是如何工作的,只说明它的工作原理。

¥Although function expressions can now be named, arrow functions always remain anonymous, which means they cannot reference themselves without being assigned to a variable first. Fortunately, in Lambda calculus there's a very good solution which allows a function to both be anonymous and self-referential. The technique is called a Y-combinator. Here we will not explain how it works, only that it works.

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 ]

注意:此方法为每次迭代分配一个新的闭包,这可能会显着增加内存使用量。这里只是为了演示可能性,但在生产中应该避免。请改用临时变量或命名函数表达式。

¥Note: This method allocates a new closure for every iteration, which may significantly increase memory usage. It's only here to demonstrate the possibility, but should be avoided in production. Use a temporary variable or a named function expression instead.

规范

Specification
ECMAScript Language Specification
# sec-arguments-exotic-objects

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看