美文网首页
不积跬步之漫谈JavaScript的递归函数

不积跬步之漫谈JavaScript的递归函数

作者: 雨飞飞雨 | 来源:发表于2019-08-25 22:54 被阅读0次

最近在看<JavaScript高级程序设计>中看到arguments.callee这个属性,才知道JavaScript里面的递归有这么多的坑。以前都不知道,今天就整理一下,我们先从最初开始吧。

用递归实现一个阶乘函数

这里就不用说定义了,咱们直接上代码,在正常模式下。

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

上面是一个典型的递归调用,可但是,它有一个问题。

var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));//报错

递归函数就是在一个函数里通过名字调用自身的情况(还是得说一下)。正常的情况下我们这样做没有问题了,可是关键是JavaScript语言和其他的语言不太一样,函数名称只是一个指针,它并不是函数的实体对象。所以如果我们把这个指针改变了,那么在函数里面的指针调用就不是它自己了,上面的例子里,我们直接把它干掉了。让它指向了空,它直接就奔溃了。

这里就引出了一个重要的属性:

arguments.callee

calleearguments对象的一个属性,arguments.callee值一个指向正在执行的函数的指针。因此它可以实现对函数的递归调用。

例如:

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

通过使用arguments.callee代理函数名,可以确保无论怎样调用函数都不会出问题。因此在正常模式下,使用这种方式会更加保险。

但是在严格模式下,调用上面的arguments.callee却会导致错误,因为严格模式不支持arguments。那怎么样办呢 ?

严格模式下无法使用arguments.callee怎么办 ?

通过使用命名表达式也可是实现同样的效果。看代码:

var factorial = (function f(num){
     if(num<=1){
        return 1;
    }else{
        return num * f(num-1);
    }
});

上面的代码创建了一个名为f()的命名表达式,然后将它赋值给变量factorial。这样的话,即使把变量的变成另一个变量,也不会影响我内部自己的调用。f函数名字仍然有效。所以递归函数调用照样能正确执行。

现在我们把ECMA的版本升级到6.

ES6的情况下呢?

ES6的情况下,我们可以写成另外的方式来实现。例如:

const factorial = (function f(num){
     if(num<=1){
        return 1;
    }else{
        return num * f(num-1);
    }
});

我们把var升级为const,让这个变量变成常量,这样变量也改变不了。我们用箭头函数来替代上面的命名函数。

const factorial =num=>{
     if(num<=1){
        return 1;
    }else{
        return num * factorial(num-1);
    }
}

常量无法更改,所以我们可以写成上面的代码。但是你以为代码就没有问题了吗???

我们这样调用一下:

factorial(100000);

然后报下面的错误,RangeError:超出了最大调用堆栈大小

RangeError: Maximum call stack size exceeded
    at factorial (H:\company_work_space\Demos\Demo1.js:40:18)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)

下一步就是怎么解决这个问题。答案是使用尾调用优化。
什么是尾调用呢?可以先看这个什么是尾调用?

相关文章

  • 不积跬步之漫谈JavaScript的递归函数

    最近在看中看到arguments.callee这个属性,才知道JavaScri...

  • 不积跬步之漫谈JavaScript的递归函数之尾递归优化

    要知道什么是尾调用,我们就要先从调用栈开始说起。 什么是调用栈 ? 调用栈((Call Stack)是一个基本的计...

  • 不积跬步--漫谈JavaScript函数式编程

    这里说一下javaScript的函数式编程,它其实和函数的特性有很大的关联,就是因为函数可以被当做变量,所以变量做...

  • 不积跬步之JavaScript的数组

    为学习<数据结构与算法>做准备,我们有必要梳理一下数组,因为我们需要它来模拟各种数据结构,如栈,列表,队列等。而实...

  • 不积跬步之JavaScript 词法作用域

    昨天圣诞节,没有写,今天写两篇。奥利给!---现在是2021-1-4 还好不算晚,才隔了几天。 词法作用域 在第一...

  • 不积跬步

    2018/10/25 星期四 晴 没想到二姐的高价小收音机比手机的辐射要强,放在床边睡觉,...

  • 不积跬步

    “不积跬步无以至千里”常用来激励。 可在自己身上发掘这句名言,却只有垃圾、肥肉和慢。 一个上午,断断续续收拾了两个...

  • 不积跬步

    生活中看起不起眼的事情,对一些人来说就是财富机遇 朋友在我们单位上班,也属于从别的单位挖过来的,老板慧眼识珠,两人...

  • 不积跬步之JavaScript什么是作用域 ?

    想到坚持这件小事,我为2020年的最后一个月的最后这段时间给自己开个好头。那就从每天坚持学习这件小事开始吧。每天一...

  • JavaScript函数之递归

    JS函数 从本篇文章开始,我们将继续回到JavaScript函数的学习。在学JS基础时我们初步学习了函数,讲解了函...

网友评论

      本文标题:不积跬步之漫谈JavaScript的递归函数

      本文链接:https://www.haomeiwen.com/subject/whokectx.html