• 浅谈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

  • 相关阅读:
    android 带图片的文本框
    Android 调用相册 拍照 实现系统控件缩放 切割图片
    Android核心分析索引
    Layout 水平平分空间、垂直平分空间
    Android中ActivityManagerService与应用程序(客户端)通信模型分析
    Android采用Movie播放GIF动画
    根据银行卡卡号判断银行
    TextView 显示效果
    实现通讯录的弹窗效果
    微软 DLinq技术来临前的国内 .NET 的 ORM 发展之局势
  • 原文地址:https://www.cnblogs.com/520BigBear/p/12780925.html
Copyright © 2020-2023  润新知