前端中設計數據結構的方面很少,最經常使用的就是對樹結構的一些操做。從某種意義上來講,前端工做自己就是和樹結構打交道的一個工做方向。畢竟,DOM就是自然的樹結構。因此如何可以良好地對樹結構進行操做,是前端工程師不可或缺的一項能力。javascript
什麼是樹結構呢?從數據結構的角度來說:css
- 樹是非線性數據結構
- 每一個節點可能會有0個或多個後代
- 每一個節點具有惟一的父節點(若是有多個父節點,那就是圖了)
樹根據節點的不一樣能夠分爲不一樣的類型,最多見的分類是:html
具體他們之間的區別這裏就不細說了,具體請查看詳情前端
下面的html結構就是一個自然的樹結構。每一個Dom節點下面,有0/1/多個子節點。java
特色: 每個對象節點,下面可能會有children,也可能沒有children
let obj = [ { id: 1, type: 'dom', children: [ { id: 2, type: 'html' } ] }, { id: 3, type: 'css', children: [ { id: 4, type: 'javascript' } ] } ];
最多見的就是抽象語法樹:node
特色: 對象的屬性下面有不一樣的屬性,每個屬性下面可能還會有不一樣的屬性
這種格式常常在數據統計中出現。react
其實在我看來,樹的結構形式有不少種,可是,前端工做中不多涉及對樹節點的修改等操做,大部分是遍歷和統計數據。git
需求場景:下面以Dom樹結構爲例:
一、須要輸出每一個節點的名稱和節點深度
三、深度優先和廣度優先都須要實現
深度優先遍歷,又叫DFS(deep first search),遍歷順序是優先遍歷節點的子節點,而後再是節點的兄弟節點。github
function DeepSearch(node, deep = 0) { const child = node.childNodes; const { nodeName } = node; console.log(`name:${nodeName},deep:${deep}`); for(let i = 0, len = child.length; i < len; i++) { DeepSearch(child[i], deep + 1); } }
function deepSearch(node, deep = 0) { const stack = []; const deepArr = []; stack.push(node); deepArr.push(0); while(stack.length !== 0){ const node = stack.shift(); const deep = deepArr.shift(); const { nodeName } = node; console.log(`name:${nodeName},deep:${deep}`); const nodes = child.childNodes; for( let i = node.length; i > 0; i--) { stack.unshift(nodes[i]); deep.unshift(deep + 1); } } }
廣度優先,正好和深度優先策略相反,先遍歷節點的兄弟節點,再遍歷子節點。數組
function BreadSearch(node, deep = 0) { const child = node.childNodes; const res = []; for(let i = 0, len = child.length; i < len; i++) { const { nodeName } = node; console.log(`name:${nodeName},deep:${deep}`); res.push(child[i]); } DeepSearch(res, deep + 1); }
function breadSearch(node, deep = 0) { const stack = []; const deepArr = []; stack.push(node); deepArr.push(0); while (stack.length !== 0 ) { const node = stack.shift(); cosnt deep = stack.shift(); const { nodeName } = node; console.log(`name:${nodeName},deep:${deep}`); for(let i = 0, len = child.length; i < len; i++) { stack.push(child[i]); } } }
前端中的樹操做,常常是生成特定的樹結構。常見的場景有生成路由和生成菜單。
下面以react-router爲例,說明:
通常狀況下,react-router的路由是下面的:
<Switch> <Route path="/home" component={A}/> <Route path="/manage" component={B}/> <Route path="/customer" component={C}/> ... ... </Switch>
可是若是全部的都按照上面的寫法,每加一個路由,都須要取在內容下面,加一個
<Route path="/extra" component={D}/>
這樣會形成代碼不容易維護,並且可讀性很差。
配置的方式總好過,每次打開路由的內部代碼修改。
const routers = [ { path: '/a', component: A }, { title: '考試', id: 'exam', path: '/b', children: [ { path: '/c', component: C }, { path: '/d', component: D } ] } ]; function getRoute (routes, rootPath = '') { let res = []; for (let i = 0, len = routes.length; i < len; i++) { const route = routes[i]; const { path } = route; if (route.children) { res = [...res, ...getRoute(route.children, path)]; } else { res.push( <Route path={`${rootPath}${path}`} ...route /> ); } } return res; }; <Switch> { getRoute(routers) } </Switch>
菜單和路由的方式很類似,並且一般會結合在一塊兒使用,具體的寫法,這裏就不贅述了,由於實在是太類似了,留給大家吧。。