JavaScript递归是除了闭包以外,函数的又一特色呢.很多开发新手都很难理解递归的原理,我在此总结出自己对递归的理解.

所谓递归,可以这样理解,就是一个函数在自身的局部环境里通过自身函数名又调用,如此反复,直到条件不满足,返回最终结果的一种情形.最简单的一个示例代码如下:

function fn(a){
   return a <= 1 ? a = 1 : a * fn(a - 1);
}

这也是一个最经典的递归阶乘函数了,虽然这行代码表面上看起来没什么问题,但在执行如下代码,则会出现错误.

var otherfn = fn;
fn = null;
otherfn(5);//出错,fn is not a function

  

以上将定义的递归阶乘函数fn()保存在变量otherfn中,然后将fn()函数设置为null,也就是说,这样就断掉了对原始函数fn()的引用,因此在调用otherfn()函数时候就会报错.毕竟调用otherfn()实际上就相当于是调用fn()函数,而fn()函数已经被解除了引用,所以自然会报一个fn 不是一个函数的错误呢.

那么,有没有办法解决呢?

JavaScript的arguments.callee就可以解决这个问题.argumens.callee实际上就是指向一个函数的指针,因此,只要将以上代码修改成如下所示:

function fn(a){
   return a <= 1 ? a = 1 : a * arguments.callee(a - 1);
}

这样,通过arguments.callee代替了函数名fn,就保证了引用不会被解除,因此无论怎么调用该函数就不会出问题了.

因此,在编写递归函数时,使用arguments.callee总是比直接用函数名更好一点.

不过,使用arguments.callee有个缺点,那就是在严格模式下,是无非访问arguments.callee的,因此就需要使用命名函数表达式来达到与这个指针带来的效果呢.代码如下:

var fn = (function f(a){
  return a <= 1 ? a = 1 : a * f(a - 1);
});

以上代码创建了一个命名为f的函数表达式,然后将它赋值给一个fn变量.因此即便把这个函数表达式赋值给另一个变量,这个函数也仍然可以正常调用.而且这种做法不仅在适合非严格模式,也同样适合严格模式.

JavaScript递归原理的更多相关文章

  1. JavaScript内部原理实践——真的懂JavaScript吗?(转)

    通过翻译了Dmitry A.Soshnikov的关于ECMAScript-262-3 JavaScript内部原理的文章, 从理论角度对JavaScript中部分特性的内部工作机制有了一定的了解. 但 ...

  2. How Javascript works (Javascript工作原理) (一) 引擎,运行时,函数调用栈

    个人总结:该系列文章对JS底层的工作原理进行了介绍. 这篇文章讲了 运行时:js其实是和AJAX.DOM.Settimeout等WebAPI独立分离开的 调用栈:JavaScript的堆内存管理 和 ...

  3. How Javascript works (Javascript工作原理) (五) 深入理解 WebSockets 和带有 SSE 机制的HTTP/2 以及正确的使用姿势

    个人总结: 1.长连接机制——分清Websocket,http2,SSE: 1)HTTP/2 引进了 Server Push 技术用来让服务器主动向客户端缓存发送数据.然而,它并不允许直接向客户端程序 ...

  4. How Javascript works (Javascript工作原理) (十一) 渲染引擎及性能优化小技巧

    个人总结:读完这篇文章需要20分钟,这篇文章主要讲解了浏览器中引擎的渲染机制. DOMtree       ----|   |---->  RenderTree CSSOMtree  ----| ...

  5. JavaScript 工作原理之十一-渲染引擎及性能优化小技巧

    原文请查阅这里,略有删减,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland. 本系列持续更新中,Github 地址请查阅这里. 这是 JavaScript 工作原理的第十一章. 迄 ...

  6. JavaScript作用域原理(三)——作用域根据函数划分

    一.一个for实例 <p id="scope3" style="color:red"></p> var pscope3 = docume ...

  7. JavaScript作用域原理(二)——预编译

    JavaScript是一种脚本语言, 它的执行过程, 是一种翻译执行的过程.并且JavaScript是有预编译过程的,在执行每一段脚本代码之前, 都会首先处理var关键字和function定义式(函数 ...

  8. JavaScript作用域原理(一)——作用域链

    一.作用域的描述 JavaScript权威指南中对作用域有一句很精辟的描述:“JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.” 在JavaScript中,作用域的 ...

  9. 深入理解JavaScript Hijacking原理

    最近在整理关于JavaScript代码安全方面的资料,在查关于JavaScript Hijacking的资料时,发现关于它的中文资料很少,故特意整理一下. 一.JavaScript Hijacking ...

随机推荐

  1. Redis进阶实践之九 独立封装的RedisClient客户端工具类

    一.引言 今天开始有关Redis学习的第九篇文章了,以后肯定会大量系统使用Redis作为缓存介质,为了更好的更好的Redis,自己写了两个工具类,但是这两个工具类,没有提供一致的接口,是为了使用的独立 ...

  2. raid制作(转载)

    Dell r710服务器,有4块450G硬盘,默认做的RAID5.我们的目的是取其中3块硬盘做RAID5,留一块硬盘做热备. 在这里,我具体解释一下 ①4块硬盘做成RAID5 ②3块硬盘做RAID5, ...

  3. 是否编码输出html字符

    template.config(name, value)方法用于更改引擎的默认配置. 其中字段escape,类型为boolean,默认为true. 首先,我们不修改配置信息输出一段带有html标签的字 ...

  4. JAVA并发编程学习笔记------锁顺序死锁

    一.需求描述: 将资金从一个账户转移到另一个账户. 二.程序实现: (1)账户类: public class Account { private long account; public Accoun ...

  5. Spring的事务管理(理论篇,下篇提供代码实现)

    事务:逻辑上的一组操作,这组操作要么全部成功,要么全部失败(事务十大特性:原子性,一致性,隔离性,持久性) 原子性:事务是不可分割的工作单位,事务中的操作要么都发生了,要么都不发生.(也就是说不能单独 ...

  6. NIO内存映射

    磁盘的IO因为速度较慢,可能成为系统运行的瓶颈.所以磁盘的IO在操作系统级实现了提前读,延迟写的机制来提升IO的性能. 提前读就是一次读取需求的数据的同时多读接下来的一段数据至OS缓冲区中,延迟写就是 ...

  7. BZOJ 2005: [Noi2010]能量采集 [莫比乌斯反演]

    题意:\((0,0)\)到\((x,y),\ x \le n, y \le m\)连线上的整点数\(*2-1\)的和 \((0,0)\)到\((a,b)\)的整点数就是\(gcd(a,b)\) 因为. ...

  8. MySQL学习记录(不断更新)

    MySQL设置数据集为UTF8仍无法输入中文的解决办法: 我们的机器默认为gbk,因此可在进入数据库之前,使用以下这条语句将默认编码改为gbk,注意没有单引号,也不要用分号. mysql -uroot ...

  9. JDBC【事务、元数据、改造JDBC工具类】

    1.事务 一个SESSION所进行的所有更新操作要么一起成功,要么一起失败 举个例子:A向B转账,转账这个流程中如果出现问题,事务可以让数据恢复成原来一样[A账户的钱没变,B账户的钱也没变]. 事例说 ...

  10. Java并发系列[5]----ReentrantLock源码分析

    在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...