遍歷在前端的應用場景很少,多數是處理
DOM
節點數或者 深拷貝。下面筆者以深拷貝爲例,簡單說明一些這兩種遍歷。 😄前端
想象有一顆節點樹,從某個頂點開始,一直往下遍歷,直到遍歷到的節點都訪問事後,往回走,遍歷沒有訪問的節點,感受很像遞歸。下面筆者就用遞歸實現 深度優先遍歷。prototype
function getRegExp(target){ var flat = ''; if(target.global) flat+='g'; if(target.ignoreCase) flat+='i'; if(target.multiline) flat+='m'; return target; } function DFS(target,visiteds){ var Type = Object.prototype.toString.call(target).slice(8,-1); console.log(Type) var copy = Type == 'Array'?[]:{}; visiteds = visiteds|| []; //處理環形數據,防止無限循環 switch(Type){ case 'Date': copy = new Date(target.getTime()); break; case 'RegExp': copy = new RegExp(target.source,getRegExp(target)); break; case 'Array': case 'Object': var index = visiteds.indexOf(target); if(index>-1){ copy = visiteds[index]; }else{ visiteds.push(target); for(var key in target){ copy[key] = DFS(target[key],visiteds); } } break; default: copy = target; break; } return copy; };
若是把
深度優先遍
歷當作縱向遍歷,那麼廣度優先遍歷
就是橫向遍歷,一層一層的往下遍歷。下面用隊列(FIFO)來實現。code
function getEmpty(o){ var Type = Object.prototype.toString.call(o).slice(8,-1); if(Type === 'Object'){ return {}; } if(Type === 'Array'){ return []; } if(Type==='Date'){ return new Date(o.getTime()); } if(Type==='RegExp'){ return new RegExp(o.source,getRegExp(o)) } return o; } function getRegExp(o){ var flat = ''; if(o.global) flat+='g'; if(o.ignoreCase) flat+='i'; if(o.multiline) flat+='m'; return o; } function BFS(target){ var queue = []; var targetMap = []; //處理環形數據,防止無限循環 var copy = getEmpty(target); if(copy!==target){ queue.push([target,copy]); } while(queue.length>0){ var [_target,_copy] = queue.shift(); //* for(var key in _target){ var index = targetMap.indexOf(_target[key]); if(index>-1){ _copy[key] = targetMap[index] continue; } _copy[key] = getEmpty(_target[key]) if(_copy[key]!==_target[key]){ queue.push([_target[key],_copy[key]]); //* targetMap.push(_target[key]); } } } return copy }
上面用兩種不一樣的方法實現了深拷貝,可是隻針對Object,Array的狀況,其餘的複雜對象沒有考慮到,固然你也能夠添加更多的處理,但筆者認爲目前這樣已經足夠用了。。。
深度優先遍歷,關鍵在於理解遞歸,而廣度優先遍歷,關鍵在於理解,queue.shift
出去的數據保存着原來數據的引用,因此纔可以在不斷的進棧出棧中修改值(間接修改值) 👊對象