今天的问题是

请问以下 alert 弹出值分别是什么?

var f = function f2() {
    return arguments.callee;
}

alert(f === f2);
alert(f === f());
alert(f() === f2());

让我们回顾 callee 的用法, Mozilla 官方的相关描述 如下:

callee is a property of the arguments local variable available
within all function objects; callee as a property of Function.arguments
is no longer used. (Function.arguments itself is also deprecated.)

arguments.callee allows anonymous functions to refer to themselves, 
which is necessary for recursive anonymous functions.

The this keyword does not refer to the currently executing function. 
Use the callee property to refer to a function within the function body. 

在 Javascript 中,一般定义某个函数有两种方式,分别是 function f() {...} 以及 var f = function () {...} 。同时,在 Javascript 中,函数可以当变量使用(Javascript 允许你结合两种方式使用,虽然这样的代码出现较少)。

var f = function f2() {
    return arguments.callee;
}

上面的代码可以分解为

function f2() {
    return arguments.callee;
}

var f = f2;

分解后的代码可以认为 f 与 f2 相同(==),但是进一步的理解他们并不完全相同(===)。调用 f() 与 f2() 执行获得的结果一样,「但 f2 这个变量保存了函数的整个内容,f 是对 f2 函数的引用」 -- form 丁坚。

其实这样的写法很少见,并不推荐在实际的项目中这样编写。说完上面的问题,回到我们的题目中。从上面的 Mozilla 的说明中可得知,arguments.callee 引用的是当前正在执行的函数本身。

所以,由此可以推断 alert(f === f()); 返回的是true;同理 f2 === f2() 返回的也是 true 。f() 返回的就是 f, f2() 返回的就是 f2, 所以第三个 alert() 等同于第一个 alert() 语法, 返回的也是 false 。

所以,本日 Javascript 测试的答案为:false、true、false 。

那么 arguments.callee 有无实际的用途?回答是肯定的。在匿名函数用得越来越多的情况下,很多时候 arguments.callee 能帮上大忙。

比如,希望某事件只执行一次,那么可以这样做(使用 YUI 框架):

YAHOO.util.Event.on(button, 'click', function(ev) {
    // ...
    // 注销自己
    YAHOO.util.Event.removeListener(button, 'click', arguments.callee);
});

YAHOO.util.Event.removeListener 的详细说明 参见这里 。我们给某个按钮注册了一个事件,时间处理器是一个匿名函数,点击一次以后,通过 removeListener 和 arguments.callee,就可以自我注销。

补充 -- form 玉伯

「也可以这样写就用不到 arguments.callee」,但这样代码感觉稍许冗余:

YAHOO.util.Event.on(button, 'click', function fn(ev) {
    // ...
    // 注销自己
    YAHOO.util.Event.removeListener(button, 'click', fn);
});

其次,arguments.callee 还有一个重要的用途,就是在递归,比如:

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

参考资料:

--EOF--