浅谈arguments属性callee

1.首先 arguments 是一个对应于传递给函数的参数的类数组对象

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

  描述

callee 是 arguments 对象的一个属性。它可以用于引用该函数的函数体内当前正在执行的函数。这在函数的名称是未知时很有用,例如在没有名称的函数表达式 (也称为“匿名函数”)内。

警告:严格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()。当一个函数必须调用自身的时候, 避免使用 arguments.callee(), 通过要么给函数表达式一个名字,要么使用一个函数声明.

为什么 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;
});

然而,这实际上是一个非常糟糕的解决方案,因为这 (以及其它的 argumentscallee, 和 caller 问题) 使得在通常的情况(你可以通过调试一些个别例子去实现它,但即使最好的代码也是次优选项,因为(JavaScript 解释器)做了不必要的检查)不可能实现内联和尾递归。另外一个主要原因是递归调用会获取到一个不同的 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 和 this。其中, arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。 请看下面这个非常经典的阶乘函数

复制代码
function factorial(num){    
   if (num <=1) {         
      return 1;     
   } else {         
   return num * factorial(num-1)     
   } 
}  
复制代码

定义阶乘函数一般都要用到递归算法;如上面的代码所示,在函数有名字,而且名字以后也不会变 的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 紧紧耦合在了一起。为 了消除这种紧密耦合的现象,可以像下面这样使用 arguments.callee

复制代码
function factorial(num){    
   if (num <=1) {         
      return 1;     
   } else {         
   return num * arguments.callee(num-1);
   } 
}  
复制代码

在这个重写后的 factorial()函数的函数体内,没有再引用函数名 factorial。这样,无论引用 函数时使用的是什么名字,都可以保证正常完成递归调用。例如

复制代码
        function factorial(num){
            if(num <= 1){
                return 1;
            }else{
                return num * arguments.callee(num-1);
            }
        }
        var trueFactorial = factorial;
        alert(trueFactorial(5));    //120    

factorial = function() { return 0; } alert(trueFactorial(5));// 120 如果没有使用arguments.callee,将返回0
复制代码

在此,变量 trueFactorial 获得了 factorial 的值,实际上是在另一个位置上保存了一个函数 的指针。然后,我们又将一个简单地返回 0的函数赋值给 factorial 变量。如果像原来的 factorial() 那样不使用 arguments.callee,调用 trueFactorial()就会返回 0。可是,在解除了函数体内的代 码与函数名的耦合状态之后,trueFactorial()仍然能够正常地计算阶乘;至于 factorial(),它现 在只是一个返回 0的函数。 

参考自js高程第三版

 

现在已经不推荐使用arguments.callee();

原因:访问 arguments 是个很昂贵的操作,因为它是个很大的对象,每次递归调用时都需要重新创建。影响现代浏览器的性能,还会影响闭包。

不能用怎么办?

像第三段中的例子,重写 factorial()方法导致trueFactorial()结果不在预期。是为了演示而做的。平时写代码应该避免。

递归时用到arguments.callee()是常见的事情,比如

一道面试题。接受参数n=5,不用for循环输出数组【1,2,3,4,5】

这用递归的思路,配合arguments.callee,代码如下

复制代码
function show(n) {
    var arr = [];
    return (function () {
        arr.unshift(n);
        n--;
        if (n != 0) {
            arguments.callee();
        }
        return arr;
    })()
}
show(5)//[1,2,3,4,5]
复制代码

现在arguments.callee 被弃用了。怎么办,其实很简单,给内部函数一个名字即可

复制代码
function show(n) {
    var arr = [];
    return (function fn() {
        arr.unshift(n);
        n--;
        if (n != 0) {
            fn();
        }
        return arr;

    })()
}
show(5)//[1,2,3,4,5]
复制代码

 参考地址:https://www.cnblogs.com/lijinwen/p/5727550.html

mdn  :           https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments/callee

原文地址:https://www.cnblogs.com/520BigBear/p/12780925.html