递归

1:找规律

2:找出口

var data = [
 {
     name: "所有物品",
     children: [
         {
             name: "水果",
             children: [{name: "苹果", children: [{name: '青苹果'}, {name: '红苹果'}]}]
         },
         {
             name: '主食',
             children: [
                 {name: "米饭", children: [{name: '北方米饭'}, {name: '南方米饭'}]}
             ]
         },
         {
             name: '生活用品',
             children: [
                 {name: "电脑类", children: [{name: '安卓电脑'}, {name: '苹果电脑'}]},
                 {name: "工具类", children: [{name: "锄头"}, {name: "锤子"}]},
                 {name: "生活用品", children: [{name: "洗发水"}, {name: "沐浴露"}]}
             ]
         }
  ]
}]

需要最后在页面上显示每一级的最后一个数据。

青苹果;红苹果;北方米饭;南方米饭;安卓电脑;苹果电脑;锄头;锤子;洗发水;沐浴露;

普通遍历的方法

var forFunction = function () {
    var str = ""
    data.forEach(function(row){
        row.children.forEach(function(row){
            row.children.forEach(function(row){
                row.children.forEach(function(row){
                    str += (row.name + ";")
                })
            })
        })
    })
    console.log(str)
}
forFunction()
//输出:青苹果;红苹果;北方米饭;南方米饭;联想电脑;苹果电脑;锄头;锤子;洗发水;沐浴露;

//可以看到,费劲半天写了4个forEach实现了要的效果,如果再加点别的什么逻辑,就很难弄了。这时候我们尝试用递归的思想实现:

递归遍历实现

var recursiveFunction = function(){
    var str = ''
    const getStr = function(list){
        list.forEach(function(row){
            if(row.children){
                getStr(row.children)
            }else {
                str += row.name + ";"
            }
        })
    }
    getStr(data)
    console.log(str)
}
recursiveFunction()
//输出:青苹果;红苹果;北方米饭;南方米饭;联想电脑;苹果电脑;锄头;锤子;洗发水;沐浴露;

//可以看到,递归的方式来实现的时候,我们只需要一个for循环,每次遍历接受到的数据,通过判断是否还有children,没有就代表是最后一级了,有就继续把children这个list传给函数继续遍历,最后就得到了我们想要的数据。

很明显,forEach的遍历的方式能实现多级的遍历,并且数据格式可以灵活一些,但是遍历的层级有限,而且对于未知层级的情况下,就无从下手了。 递归遍历,理论上,只要内存够用,你能实现任意层级的遍历,但缺点也很明显,没一个层级里面需要有固定的数据格式,否则无法遍历。

递归遍历轻松实现多个异步结果的依次输出

需要依次输出1,2,3,4,5,每个输出中间间隔1s。

//常规实现
var forTest = function () {
   console.time("for时间")
    for (let i = 0; i < 5; i++) {
        setTimeout(function () {
            console.log('for循环输出:' + (i + 1))
            if(i+ 1 === 5){
                console.timeEnd("for时间")
            }
        }, 1000 * (i + 1))
    }
}
forTest()
//用let当前作用域的特点实现了每隔1s输出,其实可以看出,是一起执行的,但是由于间隔时间不一样,导致结果是每隔一秒输出的。
//递归遍历实现
var recursiveTest = function(){
   console.time("递归时间")
    const test = function (i) {
        setTimeout(function () {
            i = i + 1
            console.log('递归输出:' + i)
            if(i < 5){
                test(i)
            }else {
                console.timeEnd("递归时间")
            }
        }, 1000)
    }
    test(0)
}
recursiveTest()
//这里利用递归实现就很好理解了,在setTimeout里面输出了之后,再开始下一个1s的输出。

总结

1.很多时候可以用递归代替循环,可以理解为递归是一种特殊的循环,但通常情况下不推荐这样做。

2.递归一般是在函数里面把函数自己给调用一遍,通过每次调用改变条件,来结束循环。

3.递归在数据格式一致,在数据层级未知的情况下,比普通的遍历更有优势。

4.递归在异步的时候,更容易理解,且更容易实现,因为可以在异步的回调里面,调用自己来实现每次都能拿到异步的结果再进行其他操作。

5.递归实现的快速排序比普通遍历实现的排序效率更好。