JavaScript递归 & 组装无限嵌套循环的数据

※递归理解:

递归:函数自己调用自己。
递归若有结束的条件,则会一直调用自己,直到满足调用结束条件后,再一层一层向上返回
若没有结束条件,则会一直调用下去,即无限递归。

  • 循环和递归的区别

循环:是有去无回,整个循环部分一直执行;
递归:是有去有回,自己调用自己后,递归语句后面的内容也会继续执行,还会一层层向上返回执行。

看到别人的blog的例子,或许能好理解些:

举个栗子,你用你手中的钥匙打开一扇门,结果去发现前方还有一扇门,紧接着你又用钥匙打开了这扇门,然后你又看到一扇们…但是当你开到某扇门时,发现前方是一堵墙无路可走了,你选择原路返回——这就是递归;

但是如果你打开一扇门后,同样发现前方也有一扇们,紧接着你又打开下一扇门…到了尽头但不会再原路返回就停在那——这就是循环。

看个代码例子,我觉得更容易理解递归的特点:

	<div class="block">
        <button onclick="baoliyu(3)">抱鲤鱼</button>
        <div id="liyuResult"></div>
    </div>
    <script>
		function baoliyu(depth,type){
            type = type?type:'liyuResult';
            console.log('抱着');
            document.getElementById(type).innerHTML += '\n抱着';
            if(!depth){
                console.log('我的小鲤鱼');
                document.getElementById(type).innerHTML += '\n我的小鲤鱼';
            }else{
                baoliyu(--depth,type);
            }
            console.log('啦='+depth);
            document.getElementById(type).innerHTML += '\n啦;depth='+depth;
        }
      </script>

执行结果:
后面的啦啦啦,也就是递归后,一层一层向上返回的表象。
在这里插入图片描述
第一个depth=0 是进入if后,没有再递归,顺序执行下去的。
第二个depth=0 及后面的depth=1和2,都是递归回归的执行。



※Js递归调用方式:

1.通过函数名递归调用

最简单的方式就是直接通过函数名调用即可,其实适合大部分简单应用场景。
例子如上:

// 调用递归函数begin
baoliyu(2)
// 调用递归函数end

缺点:
函数名是指向函数对象的指针,如果把函数的名与函数对象本身的指向关系断开,此方式将无法找到正确的指向。使用时会很脆弱。
例:
如下代码,将递归函数重新赋值给一个新的变量后,再调用。
就会出现调用自己时找不到原函数名的问题。

	<div class="block">
        <button onclick="baoliyuNew(2)">抱鲤鱼New</button>
    </div>
	<script>
		//切断函数名关联test
		var baoliyuNew = baoliyu;
        baoliyu = null;
     </script>

执行结果:
在这里插入图片描述

2.通过arguments.callee调用

例:

	<div class="block">
        <button onclick="baoliyuCallee(2)">抱鲤鱼callee</button>
        <div id="liyuResultCallee"></div>
    </div>
	<div class="block">
        <button onclick="baoliyuNew(2)">抱鲤鱼New</button>
    </div>
	<script>
		function baoliyuCallee(depth){
            console.log('抱着');
            document.getElementById('liyuResultCallee').innerHTML += '\n抱着';
            if(!depth){
                console.log('我的小鲤鱼');
                document.getElementById('liyuResultCallee').innerHTML += '\n我的小鲤鱼';
            }else{
            	// 调用递归函数begin
                arguments.callee(--depth);
                // 调用递归函数end
            }
            console.log('啦');
            document.getElementById('liyuResultCallee').innerHTML += '\n啦';
        }

		//切断函数名关联test
		var baoliyuNew = baoliyuCallee;
        baoliyuCallee = null;
     </script>

结果:
在这里插入图片描述
缺点:
虽没有上面不会被函数名的限制问题,但,
严格模式下是禁止使用arguments.callee

3.通过闭包实现

在第一个例子的基础上,加上一层闭包封装。这个方法是综合简单又好用的方式。

		// 调用递归函数begin
		function baoliyuMult(dept){
            return baoliyu(dept,'liyuResultMult');
        }
        // 调用递归函数end
        //切断函数名关联test
		var baoliyuNew = baoliyuMult;
        baoliyuMult = null;

结果:
在这里插入图片描述



※Js递归 常用的几个例子

  • 斐波那契数列
    1, 1, 2, 3, 5, 8, 13, 21, 34, … 求第n个数是多少。
		function fibonacci(n){
            if(n<=0){
                return 0;
            }
            if(n<=2){
                return 1;
            }
            return arguments.callee(n-1) + arguments.callee(n-2);
        }
        console.log('fibonacci(5)='+fibonacci(6));

在这里插入图片描述

  • 汉诺塔
    有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方。
		function hanoi(n,from,ass,to){
            if(n>0){
                hanoi(n-1,from,to,ass);
                console.log("移动第"+n+"个从"+from+"到"+to);
                hanoi(n-1,ass,from,to);
            }
        }
        hanoi(3,"A","B","C");

在这里插入图片描述

  • 最大公约数
    指两个或多个整数共有约数中最大的一个。
		// 最大公约数【辗转相除】
        function getGCD(a,b){
            var c = a%b;
            if(c!==0){
                return arguments.callee(b,c);
            }else{
                return b;
            }
        }
        console.log('getGCD='+getGCD(319,377));

在这里插入图片描述

附:
所有的demo:

https://github.com/SummerOrange/JSDemos/blob/master/recursive.html

※【实际应用】组装无限嵌套循环的值

待组装的无限循环的JSON格式如下:
就是第一层数据嵌套一个对象数组comment,第二层嵌套第三层对象数组comment。。。以此类推,无限循环。总层级数未知。

目标需求:
希望将第三层及以上的comment的数据都追加到第二层comment中。(聊天列表展示)
源JSON数据:

{
    "comment":[
        {
            "id":"1.1",
            "comment":[
                {
                    "id":"1.1.1",
                    "comment":[
                        {
                            "id":"1.1.1.1",
                            "comment":[
                                {
                                    "id":"1.1.1.1.1",
                                    "comment":[

                                    ]
                                }
                            ]
                        },
                        {
                            "id":"1.1.1.2",
                            "comment":[

                            ]
                        }
                    ]
                },
                {
                    "id":"1.1.2",
                    "comment":[

                    ]
                }
            ]
        },
        {
            "id":"1.2",
            "comment":[

            ]
        }
    ]
}

目标JSON数据:

{
    "comment":[
        {
            "id":"1.1",
            "comment":[
                {
                    "id":"1.1.1",
                    "comment":[

                    ]
                },
                {
                    "id":"1.1.2",
                    "comment":[

                    ]
                },
                {
                    "id":"1.1.1.2",
                    "comment":[

                    ]
                },
                {
                    "id":"1.1.1.1",
                    "comment":[

                    ]
                },
                {
                    "id":"1.1.1.1.1",
                    "comment":[

                    ]
                }
            ]
        },
        {
            "id":"1.2",
            "comment":[

            ]
        }
    ]
}

解析方法:

		// 调用
        let list = data //总数据源
        // 循环,为了将第二层单独剥离
          for (let i = 0; i < list.length; i++) {
            let secondList = []  //定义一个新数据,存放第三、第四。。的comment数据
            this.dealCommentSecond(list[i], secondList)  //递归调用
            list[i].comment = secondList  //将组装好的数据赋值给第二层的comment
          }
          
	  //递归方法
	  dealCommentSecond (data, secondList) {
      let rec = data.comment
      for (let i = 0; i < rec.length; i++) {
        if (rec[i] && rec[i].comment && rec[i].comment.length > 0) {
          secondList.push(rec[i])
          this.dealCommentSecond(rec[i], secondList)
        } else {
          secondList.push(rec[i])
        }
      }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值