前端中设计数据结构的方面很少,最经常使用的就是对树结构的一些操做。从某种意义上来讲,前端工做自己就是和树结构打交道的一个工做方向。毕竟,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>
菜单和路由的方式很类似,并且一般会结合在一块儿使用,具体的写法,这里就不赘述了,由于实在是太类似了,留给大家吧。。