Javascript中的树结构

前沿

    前端中设计数据结构的方面很少,最经常使用的就是对树结构的一些操做。从某种意义上来讲,前端工做自己就是和树结构打交道的一个工做方向。毕竟,DOM就是自然的树结构。因此如何可以良好地对树结构进行操做,是前端工程师不可或缺的一项能力。javascript

树结构

定义

    什么是树结构呢?从数据结构的角度来说:css

  • 树是非线性数据结构
  • 每一个节点可能会有0个或多个后代
  • 每一个节点具有惟一的父节点(若是有多个父节点,那就是图了)

分类

树根据节点的不一样能够分为不一样的类型,最多见的分类是:html

  • 二叉树
  • 二叉搜索树
  • 平衡二叉查找树
  • 红黑树

具体他们之间的区别这里就不细说了,具体请查看详情前端

前端中常见的树结构

DOM树结构

下面的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

Javascript中树结构的遍历

    其实在我看来,树的结构形式有不少种,可是,前端工做中不多涉及对树节点的修改等操做,大部分是遍历和统计数据。git

需求场景:下面以Dom树结构为例:
一、须要输出每一个节点的名称和节点深度
三、深度优先和广度优先都须要实现
  • 假定已经有了对应的树结构,子节点是childNodes(为啥不用children呢?本身去查吧)

深度优先遍历

深度优先遍历,又叫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为例,说明:

简单状况(bad)

通常状况下,react-router的路由是下面的:

<Switch>
    <Route path="/home" component={A}/>
    <Route path="/manage" component={B}/>
    <Route path="/customer" component={C}/>
    ... ...
</Switch>

可是若是全部的都按照上面的写法,每加一个路由,都须要取在内容下面,加一个

<Route path="/extra" component={D}/>

这样会形成代码不容易维护,并且可读性很差

配置的方式(better)

配置的方式总好过,每次打开路由的内部代码修改。

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>

菜单

菜单和路由的方式很类似,并且一般会结合在一块儿使用,具体的写法,这里就不赘述了,由于实在是太类似了,留给大家吧。。

参考资料

相关文章
相关标签/搜索