js遞歸遍歷講解

JavaScript的遞歸遍歷會(huì)經(jīng)常遇到,適當(dāng)?shù)倪\(yùn)用遞歸遍歷假抄,可以提高代碼性質(zhì)量朵栖。

1.某些時(shí)候遞歸能替換for循環(huán)

我們先看一下下面2個(gè)例子。

var arrList = [1,2,3,5,100,500,10000,10000,1000,10000002]
 //for循環(huán)測(cè)試
 function forTest(){
     console.time("for循環(huán)")
     for(let i in arrList){
         console.log(((arrList[i] + arrList[i]) * 5 - arrList[i])/arrList[i])
     }
     console.timeEnd("for循環(huán)")
 }
 //遞歸遍歷測(cè)試
 function recursive() {
     console.time("遞歸遍歷")
     const testFun = function (i) {
         console.log(((arrList[i] + arrList[i]) * 5 - arrList[i])/arrList[i])
         if(i == arrList.length - 1){
             return
         }
         i++
         testFun(i)
     }
     testFun(0)
     console.timeEnd("遞歸遍歷")
 }
 forTest()
 recursive()

運(yùn)行結(jié)果:


在這里插入圖片描述

可以看到剔宪,for循環(huán)去遍歷一個(gè)數(shù)組和用遞歸遍歷去遍歷同一個(gè)數(shù)組得到的結(jié)果一樣拂铡,耗時(shí)也幾乎相同。但是寫(xiě)法上有很大區(qū)別葱绒。

遞歸特點(diǎn)

每個(gè)遞歸都有一個(gè)結(jié)束遞歸的條件感帅,上例中的:if(i == arrList.length - 1){ return }。每一個(gè)遞歸都會(huì)在函數(shù)內(nèi)部把函數(shù)本身調(diào)用一次地淀,但是函數(shù)在每次運(yùn)行的時(shí)候失球,都會(huì)發(fā)生一些變化,用來(lái)觸發(fā)遞歸的結(jié)束帮毁,上例中实苞,testFun函數(shù)在內(nèi)部調(diào)用了自己,并且每次調(diào)用i的值會(huì)+1烈疚,最終觸發(fā)了結(jié)束條件黔牵,讓遞歸結(jié)束。

2.使用遞歸爷肝,可以輕松實(shí)現(xiàn)多級(jí)遍歷

我們先看下面的代碼:

var data = [
 {
     name: "所有物品",
     children: [
         {
             name: "水果",
             children: [{name: "蘋(píng)果", children: [{name: '青蘋(píng)果'}, {name: '紅蘋(píng)果'}]}]
         },
         {
             name: '主食',
             children: [
                 {name: "米飯", children: [{name: '北方米飯'}, {name: '南方米飯'}]}
             ]
         },
         {
             name: '生活用品',
             children: [
                 {name: "電腦類(lèi)", children: [{name: '聯(lián)想電腦'}, {name: '蘋(píng)果電腦'}]},
                 {name: "工具類(lèi)", children: [{name: "鋤頭"}, {name: "錘子"}]},
                 {name: "生活用品", children: [{name: "洗發(fā)水"}, {name: "沐浴露"}]}
             ]
         }
  ]
}]

某些時(shí)候猾浦,坑逼后臺(tái)讓我們遍歷上面的一個(gè)數(shù)組,最后在頁(yè)面上顯示沒(méi)一級(jí)的最后一個(gè)數(shù)據(jù)阶剑。就是下面的數(shù)據(jù):

青蘋(píng)果;紅蘋(píng)果;北方米飯;南方米飯;聯(lián)想電腦;蘋(píng)果電腦;鋤頭;錘子;洗發(fā)水;沐浴露;

我們先用遍歷的方式來(lái)實(shí)現(xiàn):

//普通遍歷實(shí)現(xiàn)
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()
//輸出:青蘋(píng)果;紅蘋(píng)果;北方米飯;南方米飯;聯(lián)想電腦;蘋(píng)果電腦;鋤頭;錘子;洗發(fā)水;沐浴露;

可以看到跃巡,前端累死累死寫(xiě)了4個(gè)forEach實(shí)現(xiàn)了產(chǎn)品要的效果,這時(shí)候如果再加點(diǎn)別的什么邏輯牧愁,就很難弄了素邪。這時(shí)候我們嘗試用遞歸的思想實(shí)現(xiàn):

//遞歸遍歷實(shí)現(xiàn)
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()
//輸出:青蘋(píng)果;紅蘋(píng)果;北方米飯;南方米飯;聯(lián)想電腦;蘋(píng)果電腦;鋤頭;錘子;洗發(fā)水;沐浴露;

可以看到,遞歸的方式來(lái)實(shí)現(xiàn)的時(shí)候猪半,我們只需要一個(gè)for循環(huán)兔朦,每次遍歷接受到的數(shù)據(jù),通過(guò)判斷是否還有children磨确,沒(méi)有就代表是最后一級(jí)了沽甥,有就繼續(xù)把children這個(gè)list傳給函數(shù)繼續(xù)遍歷,最后就得到了我們想要的數(shù)據(jù)乏奥。

很明顯摆舟,forEach的遍歷的方式能實(shí)現(xiàn)多級(jí)的遍歷,并且數(shù)據(jù)格式可以靈活一些,但是遍歷的層級(jí)有限恨诱,而且對(duì)于未知層級(jí)的情況下媳瞪,就無(wú)從下手了。
遞歸遍歷照宝,理論上蛇受,只要內(nèi)存夠用,你能實(shí)現(xiàn)任意層級(jí)的遍歷厕鹃,但缺點(diǎn)也很明顯兢仰,沒(méi)一個(gè)層級(jí)里面需要有固定的數(shù)據(jù)格式,否則無(wú)法遍歷剂碴。

3.遞歸遍歷輕松實(shí)現(xiàn)多個(gè)異步結(jié)果的依次輸出

我們先看一下下面的需求把将,需要依次輸出1,2,3,4,5,每個(gè)輸出中間間隔1s汗茄。
我們先嘗試下面的方式:

//常規(guī)實(shí)現(xiàn)
var forTest = function () {
   console.time("for時(shí)間")
    for (let i = 0; i < 5; i++) {
        setTimeout(function () {
            console.log('for循環(huán)輸出:' + (i + 1))
            if(i+ 1 === 5){
                console.timeEnd("for時(shí)間")
            }
        }, 1000 * (i + 1))
    }
}
forTest()
//每隔一秒輸出了1,2,3,4,5

上面我們用let當(dāng)前作用域的特點(diǎn)實(shí)現(xiàn)了每隔1s輸出秸弛,其實(shí)可以看出,是一起執(zhí)行的洪碳,但是由于間隔時(shí)間不一樣递览,導(dǎo)致結(jié)果是每隔一秒輸出的。
我們?cè)儆眠f歸的方式實(shí)現(xiàn):

//遞歸遍歷實(shí)現(xiàn)
var recursiveTest = function(){
   console.time("遞歸時(shí)間")
    const test = function (i) {
        setTimeout(function () {
            i = i + 1
            console.log('遞歸輸出:' + i)
            if(i < 5){
                test(i)
            }else {
                console.timeEnd("遞歸時(shí)間")
            }
        }, 1000)
    }
    test(0)
}
recursiveTest()
//每隔一秒輸出1,2,3,4,5

這里利用遞歸實(shí)現(xiàn)就很好理解了瞳腌,在setTimeout里面輸出了之后绞铃,再開(kāi)始下一個(gè)1s的輸出。
最后我們看一下運(yùn)行截圖:

輸出結(jié)果

通過(guò)截圖上的耗時(shí)來(lái)看:遞歸遍歷需要的時(shí)間更長(zhǎng)嫂侍,但是更好理解一下儿捧,而且不依賴es6的環(huán)境。

4.遞歸遍歷實(shí)現(xiàn)排序

var quickSort = function(arr) {
if (arr.length <= 1) {//如果數(shù)組長(zhǎng)度小于等于1無(wú)需判斷直接返回即可
    return arr;
}
var pivotIndex = Math.floor(arr.length / 2);//取基準(zhǔn)點(diǎn)
var pivot = arr.splice(pivotIndex, 1)[0];//取基準(zhǔn)點(diǎn)的值,splice(index,1)函數(shù)可以返回?cái)?shù)組中被刪除的那個(gè)數(shù)
var left = [];//存放比基準(zhǔn)點(diǎn)小的數(shù)組
var right = [];//存放比基準(zhǔn)點(diǎn)大的數(shù)組
for (var i = 0; i < arr.length; i++){ //遍歷數(shù)組挑宠,進(jìn)行判斷分配
    if (arr[i] < pivot) {
        left.push(arr[i]);//比基準(zhǔn)點(diǎn)小的放在左邊數(shù)組
    } else {
        right.push(arr[i]);//比基準(zhǔn)點(diǎn)大的放在右邊數(shù)組
    }
}
//遞歸執(zhí)行以上操作,對(duì)左右兩個(gè)數(shù)組進(jìn)行操作菲盾,直到數(shù)組長(zhǎng)度為<=1;
return quickSort(left).concat([pivot], quickSort(right));
};

var arr = [14, 50, 80, 7, 2, 2, 11];
console.log(quickSort(arr));

這是利用遞歸實(shí)現(xiàn)的快速排序各淀,效率很高懒鉴,有興趣的同學(xué)可以試試普通的遍歷實(shí)現(xiàn),再進(jìn)行比較碎浇。

5.總結(jié)

1.很多時(shí)候可以用遞歸代替循環(huán)临谱,可以理解為遞歸是一種特殊的循環(huán),但通常情況下不推薦這樣做奴璃。
2.遞歸一般是在函數(shù)里面把函數(shù)自己給調(diào)用一遍悉默,通過(guò)每次調(diào)用改變條件,來(lái)結(jié)束循環(huán)苟穆。
3.遞歸在數(shù)據(jù)格式一致抄课,在數(shù)據(jù)層級(jí)未知的情況下唱星,比普通的遍歷更有優(yōu)勢(shì)。
4.遞歸在異步的時(shí)候剖膳,更容易理解魏颓,且更容易實(shí)現(xiàn),因?yàn)榭梢栽诋惒降幕卣{(diào)里面吱晒,調(diào)用自己來(lái)實(shí)現(xiàn)每次都能拿到異步的結(jié)果再進(jìn)行其他操作。
5.遞歸實(shí)現(xiàn)的快速排序比普通遍歷實(shí)現(xiàn)的排序效率更好沦童。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仑濒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子偷遗,更是在濱河造成了極大的恐慌墩瞳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氏豌,死亡現(xiàn)場(chǎng)離奇詭異喉酌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)泵喘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)泪电,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人纪铺,你說(shuō)我怎么就攤上這事相速。” “怎么了鲜锚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵突诬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我芜繁,道長(zhǎng),這世上最難降的妖魔是什么骏令? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮伏社,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摘昌。我一直安慰自己速妖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布聪黎。 她就那樣靜靜地躺著备恤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪露泊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天旅择,我揣著相機(jī)與錄音惭笑,去河邊找鬼。 笑死生真,一個(gè)胖子當(dāng)著我的面吹牛沉噩,可吹牛的內(nèi)容都是我干的柱蟀。 我是一名探鬼主播川蒙,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼长已,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了术瓮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤斤斧,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后撬讽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蕊连,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡游昼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烘豌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡囚聚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出标锄,到底是詐尸還是另有隱情,我是刑警寧澤料皇,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布星压,位于F島的核電站鬼譬,受9級(jí)特大地震影響娜膘,放射性物質(zhì)發(fā)生泄漏优质。R本人自食惡果不足惜竣贪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一贾富、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦汗捡、人聲如沸淑际。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)艘蹋。三九已至,卻和暖如春女阀,著一層夾襖步出監(jiān)牢的瞬間宅荤,已是汗流浹背浸策。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工冯键, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庸汗,地道東北人惫确。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓蚯舱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親枉昏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陈肛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 React Fiber 不是一個(gè)新的東西凶掰,但在前端領(lǐng)域是第一次廣為認(rèn)知的應(yīng)用蜈亩。幾年前全新的Fiber架構(gòu)讓剛...
    這個(gè)前端不太冷閱讀 5,520評(píng)論 2 8
  • 第一章: 函數(shù)式編程主要基于數(shù)學(xué)函數(shù)和它的思想前翎。 1.1 函數(shù)與js方法: 函數(shù)是一段可以通過(guò)其名稱被調(diào)用的代碼稚配,...
    yuhuan121閱讀 11,947評(píng)論 5 8
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容港华,還有我對(duì)于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
    云之外閱讀 5,050評(píng)論 0 29
  • 33立宜、JS中的本地存儲(chǔ) 把一些信息存儲(chǔ)在當(dāng)前瀏覽器指定域下的某一個(gè)地方(存儲(chǔ)到物理硬盤(pán)中)1、不能跨瀏覽器傳輸:在...
    萌妹撒閱讀 2,084評(píng)論 0 2
  • 黑色的海島上懸著一輪又大又圓的明月灯帮,毫不嫌棄地把溫柔的月色照在這寸草不生的小島上崖技。一個(gè)少年白衣白發(fā)钟哥,悠閑自如地倚坐...
    小水Vivian閱讀 3,108評(píng)論 1 5