全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

javascript如何用递归写一个简单的树形结构示例

现在有一个数据,需要你渲染出对应的列表出来:

var data = [
 {"id":1},
 {"id":2},
 {"id":3},
 {"id":4}, 
];

var str="<ul>";

data.forEach(function(v,i){
  str+="<li><span>"+v.id+"</span></li>"
})

str="</ul>"

$(doucment).append(str);

哼,easy!

语罢,又是一道题飞来!

哦,还带了儿子来当帮手。我一个循环再一个循环,轻松带走你们

var data2 = [
  {"id":1,children:[{"id":"child11"},{"id":"child12"}]},
  {"id":2},
  {"id":3children:[{"id":"child31"},{"id":"child32"}]},
  {"id":4}, 
];


var str="<ul>";

data2.forEach(function(v,i){
  if(v.children&&v.children.length>0){
    str+="<li><span>"+v.id+"</span>";
    str+="<ul>";
    v.children.forEach(function(value,index){
       str+="<li><span>"+value.id+"</span>";
    })
    str="</ul>";
    str="</li>";

  }else{
    str+="<li><span>"+v.id+"</span></li>"
  } 
})
str="</ul>"

$(doucment).append(str);

还有谁?

var json=[
    {
     name:123,id:1
     children:[
      {
       name:453,id:456,children:[{name:789,id:777,children:[{name:"hahahqqq---qq",id:3232,children:[name:'son',id:"13132123211"]}]}]
      },
      {
       name:"Cessihshis" , id:12121
      }
     ]
    },
    {
     name:"啊啊啊11", id:12
    },
   ];

竟然把全家都带来了,看我循环循环再循环大法。

嗯,不知道他家几代同堂,我该循环几次?突然间你感觉遇到对手了。

正纳闷着,突然有人拍了一下你的肩膀,兄弟,我这里有一本递归秘籍,我看你骨骼惊奇,是个练武奇才,10块钱卖你了。

 function render(treeJson){  
  if(!Array.isArray(treeJson)||treeJson.length<=0){return ""}  
  var ul=$("<ul>");
  treeJson.forEach(function(item,i){   
   var li=$("<li><span class='treeName'>"+item.name+"</span></li>");
   if(Array.isArray(item.children)&&item.children.length>0){
    li.append(render(item.children))
   }
   ul.append(li);
  })
  return ul
 }

 $(document).append(render(json));

好了不扯了,通过递归,无需再判断数据有多少层级,只有当前数组有children并且长度大于0,函数就会递归调用自身,并且返回一个ul。

这样一来,一颗非常简陋的树就生成了,不过通常树都带有radio或者checkbox选择框,而且很多时候都需要对树的右侧进行拓展,比如加一些新增,编辑等按钮什么的,可以考虑多传一个对象作为参数。

   var checkbox={
    radio:"<label class='myTreeIcon'><input type='radio' name='selectTreeRedio'><span></span></label>",

    multi:"<input type='checkbox' name='selectTreeRedio'>"
   }


   function render(treeJson,option={type:0,expandDom:function(){}}){  
     if(!Array.isArray(treeJson)||treeJson.length<=0){return ""}  
     var {type,expandDom}=option;    
     var ul=$("<ul>");
     treeJson.forEach(function(item,i){
      var str="";
      if(type==1){
       str+=checkbox.multi
      }else if(type==2){
       str+=checkbox.radio
      }
      var li=$("<li data-id='"+item+"'>"+str+"<span class='treeName'>"+item.name+"</span></li>");
      expandDom&&expandDom(li,item);
      if(item.children&&item.children.length>0){
       li.append(render(item.children,option))
      }
      ul.append(li);
    })
    return ul
   }

   //option使用了一个默认对象,默认为不需要选择框和不需要拓展, 如果传入的type为1或者2,则生成checkbox或者radio,由于radio样式比较丑,用label包起来自己模拟选中的效果;如果传入拓展参数,则把当前的父级li以及当前的参数传入,以便进行拓展。


   $("#tree").append(render(json,{
    type:1,
    expandDom:function(el,data){
     el.append("<button>编辑</button><button>测试</button><a data-msg='"+JSON.stringify(data)+"'></a>")
    }
   }))

有时候后台返回的可能不是拼装好层级的数组,而是带有pid标识的所有数组的集合,比如:

var data = [
 {"id":2,"name":"第一级1","pid":0},
 {"id":3,"name":"第二级1","pid":2},
 {"id":5,"name":"第三级1","pid":4},
 {"id":100,"name":"第三级2","pid":3},
 {"id":6,"name":"第三级2","pid":3},
 {"id":601,"name":"第三级2","pid":6},
 {"id":602,"name":"第三级2","pid":6},
 {"id":603,"name":"第三级2","pid":6}
];

为了用递归来渲染出树来,这时,就需要我们手动来将层级装好了:

  function arrayToJson(treeArray){
  var r = [];
  var tmpMap ={};

  for (var i=0, l=treeArray.length; i<l; i++) {
   // 以每条数据的id作为obj的key值,数据作为value值存入到一个临时对象里面
   tmpMap[treeArray[i]["id"]]= treeArray[i]; 
  } 

  for (i=0, l=treeArray.length; i<l; i++) {
   var key=tmpMap[treeArray[i]["pid"]];
   
   //循环每一条数据的pid,假如这个临时对象有这个key值,就代表这个key对应的数据有children,需要Push进去
   if (key) {
    if (!key["children"]){
      key["children"] = [];
      key["children"].push(treeArray[i]);
    }else{
     key["children"].push(treeArray[i]);
    }    
   } else {
    //如果没有这个Key值,那就代表没有父级,直接放在最外层
    r.push(treeArray[i]);
   }
  }
  return r
  }

现在我们已经实现了将没有层级结构的数组转化为带有层级的数组,那么问题来了,树形图还常常会需要带搜索功能,有没有办法把带层级结构的数组转化为不带层级结构的一个数组呢?因为如果不带层级的话,进行搜索等操作时就非常方便,一个filter基本就可以搞定了。

  var jsonToArray=function (nodes) {
   var r=[];
   if (Array.isArray(nodes)) {
    for (var i=0, l=nodes.length; i<l; i++) {
     r.push(nodes[i]);
     if (Array.isArray(nodes[i]["children"])&&nodes[i]["children"].length>0)
      //将children递归的push到最外层的数组r里面
      r = r.concat(jsonToArray(nodes[i]["children"]));
       delete nodes[i]["children"]
    }
   } 
   return r;
  }

这样,不管后台返回什么格式给我们,我们都可以自由的互转了,不管是带层级的转不带层级的,还是把不带层级的转化为带有层级的,都只需要调用一个函数就可以轻松解决。

不过这里有一个隐患,就是由于对象的引用关系,操作后虽然返回了我们需要东西,但是会改变原来的数据。

为了不影响到原来的数据,我们需要复制一份数据,需要进行一次深拷贝。

为什么是深拷贝而不是浅拷贝?因为浅拷贝只会复制最外面的一层,假入某一个key值里面又是一个对象,那对复制后的对象的这个key的值进行操作通用会影响到原来的对象。浅拷贝的方法有很多,ES6的assign,jq第一个参数不为true的 $.extend(),数组的slice(0),还有很多很多。

对于标准的json格式的对象,可以用JSON.parse(JSON.stringify(obj))来实现。当然,本文写的是递归,所以还是来手写一个

function deepCopy(obj){
  var object;
  if(Object.prototype.toString.call(obj)=="[object Array]"){  
   object=[];
   for(var i=0;i<obj.length;i++){
    object.push(deepCopy(obj[i]))
   }  
   return object
  }

  if(Object.prototype.toString.call(obj)=="[object Object]"){  
   object={};
   for(var p in obj){
    object[p]=obj[p]
   }  
   return object
  }
 }

其实有点类似于浅拷贝,浅拷贝会复制一层,那么我们判断某个值是对象,通过递归再来一次(好比饮料中奖再来一瓶一样,如果中奖了,就递归再来一瓶,又中奖就又递归再来一瓶,直到不再中奖),也就是说我们通过无尽的浅拷贝来达到复制一个完全的新的对象的效果。

这样,对树结构操作时,只需要传入深拷贝后新对象,就不会影响原来的对象了;

jsonToArray(deepCopy(data));

亦或是

arrayToJson(deepCopy(data)):

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# js  # 递归树形结构  # 树形结构递归  # java、js中实现无限层级的树形结构方法(类似递归)  # JavaScript树形结构数组处理之递归问题  # JavaScript递归操作树形结构代码示例  # 递归  # 不带  # 再来  # 转化为  # 又是  # 不需要  # 影响到  # 装好  # 就可以  # 的是  # 就会  # 是个  # 来了  # 好了  # 最外层  # 放在  # 第一个  # 那就  # 有很多  # 可以用 


相关文章: 建站之星免费模板:自助建站系统与智能响应式一键生成  网站制作模板下载什么软件,ppt模板免费下载网站?  网站专业制作公司有哪些,做一个公司网站要多少钱?  建站主机选哪种环境更利于SEO优化?  如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?  如何在建站主机中优化服务器配置?  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  如何打造高效商业网站?建站目的决定转化率  如何选择高效可靠的多用户建站源码资源?  如何在阿里云服务器自主搭建网站?  企业微网站怎么做,公司网站和公众号有什么区别?  如何用IIS7快速搭建并优化网站站点?  c++怎么实现高并发下的无锁队列_c++ std::atomic原子变量与CAS操作【详解】  宝塔Windows建站如何避免显示默认IIS页面?  如何通过商城自助建站源码实现零基础高效建站?  专业网站制作服务公司,有哪些网站可以免费发布招聘信息?  如何通过cPanel快速搭建网站?  公司网站制作价格怎么算,公司办个官网需要多少钱?  宝塔建站后网页无法访问如何解决?  *服务器网站为何频现安全漏洞?  如何通过商城免费建站系统源码自定义网站主题?  建站之星如何助力企业快速打造五合一网站?  小自动建站系统:AI智能生成+拖拽模板,多端适配一键搭建  家具网站制作软件,家具厂怎么跑业务?  建站主机选购指南与交易推荐:核心配置解析  Java解压缩zip - 解压缩多个文件或文件夹实例  如何在宝塔面板创建新站点?  成都响应式网站开发,dw怎么把手机适应页面变成网页?  南平网站制作公司,2025年南平市事业单位报名时间?  高端网站建设与定制开发一站式解决方案 中企动力  英语简历制作免费网站推荐,如何将简历翻译成英文?  建站168自助建站系统:快速模板定制与SEO优化指南  小程序网站制作需要准备什么资料,如何制作小程序?  如何通过可视化优化提升建站效果?  洛阳网站制作公司有哪些,洛阳的招聘网站都有哪些?  香港服务器租用费用高吗?如何避免常见误区?  建站VPS能否同时实现高效与安全翻墙?  安云自助建站系统如何快速提升SEO排名?  宝塔建站教程:一键部署配置流程与SEO优化实战指南  如何在阿里云完成域名注册与建站?  昆明网站制作哪家好,昆明公租房申请网上登录入口?  建站之星如何开启自定义404页面避免用户流失?  如何选择服务器才能高效搭建专属网站?  linux top下的 minerd 木马清除方法  官网自助建站系统:SEO优化+多语言支持,快速搭建专业网站  如何选择网络建站服务器?高效建站必看指南  建站之星如何快速解决建站难题?  早安海报制作网站推荐大全,企业早安海报怎么每天更换?  如何在Golang中实现微服务服务拆分_Golang微服务拆分与接口管理方法  深圳 网站制作,深圳招聘网站哪个比较好一点啊? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。