JavaScript
的遞歸遍歷會常常遇到,適當的運用遞歸遍歷,能夠提升代碼性質量。javascript
咱們先看一下下面2個例子。前端
var arrList = [1,2,3,5,100,500,10000,10000,1000,10000002] //for循環測試 function forTest(){ console.time("for循環") for(let i in arrList){ console.log(((arrList[i] + arrList[i]) * 5 - arrList[i])/arrList[i]) } console.timeEnd("for循環") } //遞歸遍歷測試 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()
運行結果:vue
能夠看到,for循環去遍歷一個數組和用遞歸遍歷去遍歷同一個數組獲得的結果同樣,耗時也幾乎相同。可是寫法上有很大區別。
每一個遞歸都有一個結束遞歸的條件,上例中的:if(i == arrList.length - 1){ return }
。每個遞歸都會在函數內部把函數自己調用一次,可是函數在每次運行的時候,都會發生一些變化,用來觸發遞歸的結束,上例中,testFun
函數在內部調用了本身,而且每次調用i
的值會+1,最終觸發告終束條件,讓遞歸結束。java
咱們先看下面的代碼:git
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: "沐浴露"}]} ] } ] }]
某些時候,坑逼後臺讓咱們遍歷上面的一個數組,最後在頁面上顯示每一級的最後一個數據。就是下面的數據:es6
青蘋果;紅蘋果;北方米飯;南方米飯;聯想電腦;蘋果電腦;鋤頭;錘子;洗髮水;沐浴露;
咱們先用遍歷的方式來實現:github
//普通遍歷實現 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() //每隔一秒輸出了1,2,3,4,5
上面咱們用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() //每隔一秒輸出1,2,3,4,5
這裏利用遞歸實現就很好理解了,在setTimeout
裏面輸出了以後,再開始下一個1s的輸出。
最後咱們看一下運行截圖:
經過截圖上的耗時來看:遞歸遍歷須要的時間更長,可是更好理解一下,並且不依賴es6的環境。
var quickSort = function(arr) { if (arr.length <= 1) {//若是數組長度小於等於1無需判斷直接返回便可 return arr; } var pivotIndex = Math.floor(arr.length / 2);//取基準點 var pivot = arr.splice(pivotIndex, 1)[0];//取基準點的值,splice(index,1)函數能夠返回數組中被刪除的那個數 var left = [];//存放比基準點小的數組 var right = [];//存放比基準點大的數組 for (var i = 0; i < arr.length; i++){ //遍歷數組,進行判斷分配 if (arr[i] < pivot) { left.push(arr[i]);//比基準點小的放在左邊數組 } else { right.push(arr[i]);//比基準點大的放在右邊數組 } } //遞歸執行以上操做,對左右兩個數組進行操做,直到數組長度爲<=1; return quickSort(left).concat([pivot], quickSort(right)); }; var arr = [14, 50, 80, 7, 2, 2, 11]; console.log(quickSort(arr));
這是利用遞歸實現的快速排序,效率很高,有興趣的同窗能夠試試普通的遍歷實現,再進行比較。
線上體驗地址
全部的源碼均可以在個人倉庫地址:下載
我的博客:訪問
學習如逆水行舟,不進則退,前端技術飛速發展,若是天天不堅持學習,就會跟不上,我會陪着你們,天天堅持推送博文,跟你們一同進步,但願你們能關注我,第一時間收到最新文章。
我的公衆號: