Join MDN and developers like you at Mozilla's View Source conference, 12-14 September in Berlin, Germany. Learn more at https://viewsourceconf.org

arguments.callee

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

描述

callee 是 arguments 对象的属性在该函数的函数体内,它可以指向当前正在执行的函数。当函数是匿名函数时,这是很有用的, 比如没有名字的函数表达式 (也被叫做"匿名函数")。

注意:在给函数表达式一个名称或者使用函数声明而该函数必须调用自己时,禁止使用 arguments.callee()
警告:在 ECMAScript 第五版 (ES5) 的 严格模式 中禁止使用 arguments.callee()。

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

递归函数必须能够引用它本身。很典型的,函数通过自己的名字调用自己。然而,匿名函数 (通过 函数表达式 或者 函数构造器 创建) 没有名称。因此如果没有可访问的变量指向该函数,唯一能引用它的方式就是通过 arguments.callee

下面的例子定义了一个函数,按流程,定义并返回了一个阶乘函数。该例并不是很实用,并且几乎都能够用 命名函数表达式 实现同样结果的例子, and there are nearly no cases where the same result cannot be achieved with .

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

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

为什么 arguments.callee 从 ES5 严格模式中移除掉 ?

(改编自 a Stack Overflow answer by olliej)

早期的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, 和 caller 问题) 使得在通常的情况(你可以通过调试一些个别例子去实现它,但即使最好的代码是最理想的,你也没必要去检查调试它)不可能实现内联和尾递归。另外一个主要原因是递归调用会获取到一个不同的 this 值,如:

var global = this;

var sillyFunction = function (recursed) {
    if (!recursed) { return arguments.callee(true); }
    if (this !== global) {
        alert("This is: " + this);
    } else {
        alert("This is the global");
    }
}

sillyFunction();

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

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

这有很多好处:

  • 函数能够从你代码内部调用
  • 不会在外部作用域创建一个变量 (除了 IE 8 及以下)
  • 性能比访问 arguments 对象更好

另外一个被废弃的特性是 arguments.callee.caller,具体点说则是 Function.caller。为什么? 额,在任何一个时间点,你能在堆栈中找到任何函数的最深层的调用者,也正如我在上面提到的,在调用堆栈有一个单一重大影响:不可能做大量的优化,或者有更多更多的困难。比如,如果你不能保证一个函数 f 不会调用一个未知函数,它就绝不可能是内联函数 f。基本上这意味着内联代码中积累了大量防卫代码:

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

如果JavaScript解释器不能保证所有提供的参数数量在被调用的时候都存在,那么它需要在行内代码插入检查,或者不能内联这个函数。现在在这个特殊例子里一个智能的解释器应该能重排检查而更优,并检查任何将不用到的值。然而在许多的情况里那是不可能的,也因此它不能够内联。 

使用arguments.callee时,一个没有良好替代方案的用例

然而,在下面的例子中没有可以替代 arguments.callee 的方案,因此弃用它会有一个BUG (参看 bug 725398):

function createPerson (sIdentity) {
    var oPerson = new Function("alert(arguments.callee.identity);");
    oPerson.identity = sIdentity;
    return oPerson;
}

var john = createPerson("John Smith");

john();

Specifications

Specification Status Comment
ECMAScript 1st Edition (ECMA-262) Standard Initial definition. Implemented in JavaScript 1.2
ECMAScript 5.1 (ECMA-262)
Arguments Object
Standard  
ECMAScript 2015 (6th Edition, ECMA-262)
Arguments Exotic Objects
Standard  
 

Browser compatibility

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support (Yes) (Yes) (Yes) (Yes) (Yes)
Feature Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support (Yes) (Yes) (Yes) (Yes) (Yes) (Yes)

See also

 

文档标签和贡献者

 此页面的贡献者: jonkee, Ende93, teoli, gemstone
 最后编辑者: jonkee,