Javascript中的arguments.callee有什么作用?

29
我还没有找到这个变量的完整跨浏览器文档。 arguments.callee 是用来做什么的?它是如何工作的?
它有哪些参数?

其他人指出了arguments.callee是什么(并指向MDN,该网站对此有很好的介绍)-您可以在调用自身的递归函数中使用arguments.callee。 - Jack
那(以及所有其他事情)都是对callee的不良使用。给函数命名并使用该名称调用它会更好,也更不容易出错。 - Mathias Schwarz
如果你所说的“which arguments does it have”是指函数期望哪些参数,答案是:函数在使用arguments.callee时期望哪些参数,而arguments.callee.length会告诉你这一点。奇怪的是,没有人指出这一点:跨浏览器是没问题的,即使IE8也支持,但是请注意:在严格模式下会抛出错误。 - Elias Van Ootegem
6个回答

31

arguments.callee是一个指向当前正在被调用函数的引用。首先,不要使用它:如果你处于严格模式下,它只会抛出错误。

然而,就我个人而言 - 我不是唯一这样认为的(参见这里)- 我会想念这个属性。在解释为什么之前,我将给您一个伪例子,说明何时可能会使用它:

var looper = (function(someClosureVar)
{
    setTimeout((function(resetTimeout)
    {
        return function()
        {
            //do stuff, stop OR:
            resetTimeout();
        };
    }(arguments.callee)),1000);
}(document.getElementById('foobar')));

我希望你喜欢闭包,因为我很喜欢 - 而这就是 arguments.callee 很可能出现的地方。倒数第二行是最重要的:

(arguments.callee)

这是一个指向匿名函数的引用,该匿名函数在闭包范围内设置了初始超时时间(在这种情况下仅访问1个DOM元素)。匿名函数在返回后会被垃圾回收,但在这种情况下,我将其添加到超时回调的作用域中(将其作为参数传递给另一个返回实际回调函数的匿名函数),因此它仍然被某处引用。
现在,如果您使用严格模式,您不必担心,因为代码在严格模式下看起来像这样:

var looper = (function tempName(someClosureVar)
{
    setTimeout((function(resetTimeout)
    {
        return function()
        {
            //do stuff, stop OR:
            resetTimeout();
        };
    }(tempName)),1000);
}(document.getElementById('foobar')));

给函数命个名就行了。为什么我不喜欢它呢?arguments.callee会引起警报,就像一些闭包技巧正在发生的匿名函数一样。我想这只是一种习惯,但我觉得这种习惯有助于更轻松地结构化和调试我的代码。
再加上对IE的病态憎恨,这对于任何进行客户端脚本编写的人都是自然而然的。不支持严格模式的IE版本倾向于向全局命名空间泄漏函数名称,因此永远不允许与函数(和我们创建的闭包)相关联的内存被GC回收。这可能导致循环引用,更糟糕的是,循环DOM引用,这可能导致内存泄漏。

实际上:这里有另一个真实的例子,其中使用了 arguments.callee:事件委托和分离事件侦听器
这里有关于JS严格模式和使用arguments.callee的递归的更多信息。

最后一个问题,我认为最明显的例子是:arguments.callee如何方便我们进行递归替换函数:

function someF(foo)
{
    //'use strict'; <-- would throw errors here
    foo = foo.replace(/(a|b)+/gi, function (p1,p2)
    {
        if (p1.match(/(a|b){2,}/i))
        {
            return p1.replace(/(a|b)/gi,arguments.callee);//recursive
        }
        return (p2.match(/a/i) ? 'X':'Y');
    });
}

如所请求,MDN 上关于 arguments.callee 的文档指出其在严格模式下用法会受到限制(ECMA 5 规范,这也是为什么 DC 说 arguments.callee 已被弃用的原因)
同时还有更多关于严格模式的内容


@JordiP.S.: 我在答案底部添加了2个链接,均为MDN。 - Elias Van Ootegem
在 getElementById('foobar') 后的第一个代码块末尾缺少一个闭合括号。 - Frug
1
@Frug:已编辑,我可以补充一下:发现得很好。我还改变了IIFE的写法,将调用括号移到了分组运算符内部。 - Elias Van Ootegem

5

它指定了当前正在执行的函数,因此arguments.callee是当前函数。如果您需要在匿名函数中进行递归,这可能会有所帮助。以下是来自Mozilla的示例:

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)

4
Callee是ECMAScript 3标准的一部分,因此在跨浏览器使用时应该是安全的。 Callee保存当前正在执行的函数,并调用它将调用当前函数。因此,callee接受与封闭函数完全相同的参数(或者更确切地说,它就是当前函数)。这里提供了更多信息:https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee 使用callee被认为是不良风格。相反,给您的函数命名并使用该名称。

3

arguments.callee是一种通过询问“谁在调用这个特定参数?”来知道当前执行函数的迂回方式。. . . .

 function factorial(a){
    if(a>0)
      return a*arguments.callee(a-1);
 }

如果调用factorial(5),它将检查条件是否大于0,如果为真,则执行下一个较小数字的相同逻辑……在某些情况下,您不知道要调用的函数的名称……因此,您可以使用此属性。
这里有一个很好的参考。 来自MDN的arguments.callee 更新:ES5中已弃用arguments.callee()。

支持什么? - ekkis

2
`callee`是`arguments`对象的一个属性。它可以用于在该函数的函数体内引用当前正在执行的函数。MDN文档

0

arguments.callee 是一个自身的 function

function init(name){
   if(name != ""){
     console.log('name',name)
   }else{
     console.log('type', typeof arguments.callee) // function
     console.log("who am i :\n", arguments.callee)
     arguments.callee('john')
   }
}
init("")


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接