摘要:也正是引用類(lèi)型的數(shù)據(jù)的這個(gè)特點(diǎn),保證了我們的無(wú)論多少層的子元素都能被正確的穿到了對(duì)應(yīng)的父元素上五總結(jié)丈高樓始于平地,打好基礎(chǔ)知識(shí)異常重要文章出自成都社區(qū),歡迎大家的加入,和我們一起討論學(xué)習(xí)
一、問(wèn)題描述
相信做前端的小伙伴都有遇到過(guò)將一個(gè)平鋪的 ‘樹(shù)’ 結(jié)構(gòu)轉(zhuǎn)換成一個(gè)真正的 ‘樹(shù)’ 結(jié)構(gòu),比如說(shuō)下面這種:
var _JSON_ = [ {id: 7, name: "豬", pid: 2}, {id: 8, name: "牛", pid: 2}, {id: 9, name: "羊", pid: 2}, {id: 13, name: "三黃雞", pid: 4}, {id: 14, name: "白羽雞", pid: 4}, {id: 15, name: "火雞", pid: 4}, {id: 4, name: "雞", pid: 1}, {id: 5, name: "鴨", pid: 1}, {id: 6, name: "鵝", pid: 1}, {id: 10, name: "粟", pid: 3}, {id: 11, name: "稻", pid: 3}, {id: 12, name: "黍", pid: 3}, {id: 1, name: "禽"}, {id: 2, name: "獸"}, {id: 3, name: "谷"} ];
最終要轉(zhuǎn)換成類(lèi)似如下的格式,方便在頁(yè)面渲染:
[ {id: 1, name: "禽", pid: 0, children: [ {id: 4, name: "雞", pid: 1, children: [ {id: 13, name: "三黃雞", pid: 4}, {id: 14, name: "白羽雞", pid: 4}, {id: 15, name: "火雞", pid: 4} ]}, {id: 5, name: "鴨", pid: 1, children: []}, {id: 6, name: "鵝", pid: 1, children: []} ]}, {id: 2, name: "獸", pid: 0, children: [ {id: 7, name: "豬", pid: 2, children: []}, {id: 8, name: "牛", pid: 2, children: []}, {id: 9, name: "羊", pid: 2, children: []} ]}, {id: 3, name: "谷", pid: 0, children: [ {id: 10, name: "粟", pid: 3, children: []}, {id: 11, name: "稻", pid: 3, children: []}, {id: 12, name: "黍", pid: 3, children: []} ]} ]
你的方法是什么樣的呢?思考中...
二、代碼鑒賞相信有的小伙伴會(huì)是和網(wǎng)上大多數(shù)能搜到的答案一樣,用好幾個(gè)循環(huán)來(lái)實(shí)現(xiàn),在這里給大家解讀一下,我認(rèn)為看到代碼最少的一種解決方案,該方案出自FCC成都社區(qū)的水歌之手,Jsbin代碼地址:https://jsbin.com/budapagito/...
//十一行的代碼實(shí)現(xiàn)將 ’平鋪的樹(shù)’ 轉(zhuǎn)換為 ‘立體的樹(shù)’ 結(jié)構(gòu) function Array2Tree() { var TempMap = { }; $.each($.extend(true, [ ], arguments[0]), function () { var _This_ = TempMap[ this.id ]; _This_ = TempMap[ this.id ] = _This_ ? $.extend(this, _This_) : this; this.pid = this.pid || 0; var _Parent_ = TempMap[ this.pid ] = TempMap[ this.pid ] || { }; (_Parent_.children = _Parent_.children || [ ]).push(_This_); }); return TempMap[0].children; } console.log(JSON.stringify( Array2Tree(_JSON_), null, 4 ));三、知識(shí)點(diǎn)分析
在看一段代碼時(shí),我們首先要了解里面涉及到的知識(shí)點(diǎn)(從方法入口開(kāi)始):
1、JSON.stringify(Array2Tree(_JSON_), null, 4)
將Array2Tree(_JSON_)這個(gè)函數(shù)返回的數(shù)據(jù)處理成Json,"4"代表縮進(jìn)4空白字符串,用于美化輸出(pretty-print)
2、arguments[0]
arguments對(duì)象是所有函數(shù)中可用的局部變量。你可以使用arguments對(duì)象在函數(shù)中引用函數(shù)的參數(shù)。此對(duì)象包含傳遞給函數(shù)的每個(gè)參數(shù)的條目,第一個(gè)條目的索引從0開(kāi)始。這里的arguments[0]實(shí)際就是取得我們傳入函數(shù)的_JSON_數(shù)組。
3、$.extend()
描述:將兩個(gè)或更多對(duì)象的內(nèi)容合并到第一個(gè)對(duì)象。
也可以是$.extend(boolean,dest,src1,src2,src3...)
第一個(gè)參數(shù)boolean代表是否進(jìn)行深度拷貝不含第一個(gè)參數(shù)boolean,它的含義是將src1,src2,src3...合并到dest中,返回值為合并后的dest,由此可以看出該方法合并后,是修改了dest的結(jié)構(gòu)的。所以這里$.extend(true, [ ], arguments[0])的意思就是把傳的_JSON_數(shù)組合并到一個(gè)空的數(shù)組 [ ] 上去, 保證后續(xù)的操作不會(huì)改變arguments[0]的結(jié)構(gòu)。
備注:$.extend(true, [ ], arguments[0]) , 也是可以直接遍歷arguments[0]:
4、$.each()
jQuery的each方法是跟each的語(yǔ)義一樣是遍歷的作用。
當(dāng)我們第一參數(shù)是Array時(shí):
$.each(Array, function(key, value){ this; // 這里的this和value一樣都是指向每次遍歷Array中的當(dāng)前元素 })
5、_This_ = TempMap[ this.id ] = This ? $.extend(this, _This_) : this;
這個(gè)里面包含兩個(gè)知識(shí)點(diǎn):
三目運(yùn)算符: let variable = a ? b : c 即: a 可以是任意可以轉(zhuǎn)換成boolean類(lèi)型的值或者運(yùn)算,如果a為true的話(huà),上式等同于let variable = b; 否則 上式等同于let variable = c;
a = b = c : 等同于 b = c, a = b(注:只有 a 是可以在這里聲明變量的)。
6、邏輯或( a || b )運(yùn)算的妙用
邏輯或運(yùn)算( a || b ),其中a、b可以是 boolean 類(lèi)型或者任意能轉(zhuǎn)換成 boolean 類(lèi)型的數(shù)據(jù)類(lèi)型或者運(yùn)算。在此段代碼中巧妙的運(yùn)用到了變量的初始化上。a || b 運(yùn)算的執(zhí)行過(guò)程,只有當(dāng) a 為 false 時(shí) 才會(huì)執(zhí)行 b, 只有 a 和 b 兩都是 false 會(huì)返回 false,否則返回a 或者 b,取決于 a 是否是true 或者是否可以轉(zhuǎn)換為true。
四、思路分析補(bǔ)充個(gè)基礎(chǔ)知識(shí):在 js 的邏輯判斷中 null, 0, undefined, "", "" 都可以轉(zhuǎn)換為 false。
在 Array2Tree 函數(shù)作用域內(nèi)聲明一個(gè) TempMap 的變量名,用于每項(xiàng)數(shù)據(jù)引用的臨時(shí)存儲(chǔ)
使用 $.each() 函數(shù)對(duì) $.extend(true, [ ], arguments[0]) 得到的新數(shù)組進(jìn)行遍歷,$.each() 的第二個(gè)參數(shù)是一個(gè)匿名 function(){}, 我們?cè)?function(){} 里對(duì)每個(gè)數(shù)據(jù)進(jìn)行處理,最終放置到變量 TempMap 中
在 function 的作用域中,this 指向每次遍歷中 Array 的當(dāng)前元素。比如說(shuō)第一次進(jìn)入 function() 中的 this就是:{id :7, name: "豬", pid: 2}
var _This_ = TempMap[ this.id ]; // 尋找 TempMap 對(duì)象中 key 為 this.id 的對(duì)應(yīng)值。因?yàn)槊恳粋€(gè)數(shù)據(jù)的id是唯一的,所以這里的_This_得到的值只有兩種可能: undefined 或者 { children:[object ...] }(這種情況是由后面的代碼賦值而生成的)
_This_ = TempMap[ this.id ] = _This_ ? $.extend( this, _This_ ) : this;
// 如果在 TempMap 中沒(méi)有找到 key 為 this.id 對(duì)應(yīng)的值,也就是 This = undefined 的情況,則把 this 直接賦值到 TempMap[ this.id ] 中去,并且讓 This 指向 this
// 如果找到了,就合并 This 到 this 對(duì)象上,然后再賦值給 TempMap[ this.id ],最后讓 This 指向 this。具體合并的效果可以看下面的例子:
$.extend({id: 4, name: "雞", pid: 1}, {children: {id: 13, name: "三黃雞", pid: 4}}) 結(jié)果:{ id: 4, name: "雞", pid: 1, children: { id: 13, name: "三黃雞", pid: 4 } }
重要:這一步保證當(dāng)前遍歷的元素之前的子元素能給 "穿" 到 TempMap[ this.id ] 上 ( ‘穿’ 理解成穿針引線一般的感覺(jué))。
this.pid = this.pid || 0;
// 獲取當(dāng)前被遍歷的元素的 pid, 沒(méi)有 pid 的默認(rèn)為第一層,并賦予 this.pid = 0。這里不一定非得是0,只要能和別的id區(qū)分開(kāi)來(lái)就可以,這里采用0,是因?yàn)閿?shù)據(jù)庫(kù)的索引一般從1開(kāi)始計(jì)數(shù)。
var _Parent_ = TempMap[ this.pid ] = TempMap[ this.pid ] || { };
// 判斷 TempMap[ this.pid ] 是否是 undefined 。如果 TempMap[ this.pid ] 是 undefined,則 給TempMap[ this.pid ]賦值為{},并且把 Parent 初始化為 {}。否則 TempMap[ this.pid ] 不是 undefined時(shí),則把 Parent 指向 TempMap[ this.pid ]。
( _Parent_.children = _Parent_.children || [ ] ).push( _This_ );
// 因?yàn)橄啾榷再x值運(yùn)算的優(yōu)先級(jí)相對(duì)別的要低一些,所以采取 ( Parent_.children = _Parent_.children || [ ] ) 方式保證 _Parent_.children 始終不是 undefined,并且是 array 類(lèi)型。在這個(gè)條件下,我們把 _This 存進(jìn)_Parent_.children
重要:在這一步保證當(dāng)前遍歷的元素能被 ‘穿’ 到對(duì)應(yīng)的父元素上去。
return TempMap[ 0 ].children;
// 最終 TempMap 在本列中會(huì)變成如下形式:
![一個(gè) key 為 0, 1, ... 14, 15 的 Object][5]
而展開(kāi)之后,我們會(huì)發(fā)現(xiàn)想要的 ‘真正的樹(shù)’ 就是TempMap[ 0 ].children,效果見(jiàn)本文的第二張圖。那這又是什么樣的結(jié)構(gòu)呢?可以這么說(shuō) TempMap[ 0 ].children 是這棵樹(shù)結(jié)構(gòu)的整體,而其余的1 至 15 是每個(gè)對(duì)應(yīng)的this.id 的分支。
五、總結(jié)補(bǔ)充一點(diǎn):為什么在_Parent_.children賦值后,我們的TempMap[ this.pid ]也隨之改變,這里就涉及到引用數(shù)據(jù)的知識(shí)點(diǎn)了。在這里因?yàn)開(kāi)Parent_ = TempMap[ this.pid ],所以它們來(lái)指向同一個(gè)內(nèi)存空間,在_Parent_改變后,內(nèi)存空間中的值也就改變了,所以TempMap[ this.pid ]的值也就相應(yīng)的改變了。也正是引用類(lèi)型的數(shù)據(jù)的這個(gè)特點(diǎn),保證了我們的無(wú)論多少層的子元素都能被正確的 ‘穿’ 到了對(duì)應(yīng)的父元素上
丈高樓始于平地,打好基礎(chǔ)知識(shí)異常重要!
文章出自 FCC(freeCodeCamp) 成都社區(qū),歡迎大家的加入,和我們一起討論、學(xué)習(xí)~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84439.html
摘要:第二篇仿寫(xiě)生態(tài)系列模板小故事本次任務(wù)承上完成第一篇未完成的熱更新配置核心完成模板解析模塊的相關(guān)編寫(xiě)很多文章對(duì)模板的解析闡述的都太淺了本次我們一起來(lái)深入討論一下盡可能多的識(shí)別用戶(hù)的語(yǔ)句啟下在結(jié)構(gòu)上為雙向綁定等模塊的編寫(xiě)打基礎(chǔ)最終效果圖一模板頁(yè) ( 第二篇 )仿寫(xiě)Vue生態(tài)系列___模板小故事. 本次任務(wù) 承上: 完成第一篇未完成的熱更新配置. 核心: 完成模板解析模塊的相關(guān)編寫(xiě), ...
摘要:第二篇仿寫(xiě)生態(tài)系列模板小故事本次任務(wù)承上完成第一篇未完成的熱更新配置核心完成模板解析模塊的相關(guān)編寫(xiě)很多文章對(duì)模板的解析闡述的都太淺了本次我們一起來(lái)深入討論一下盡可能多的識(shí)別用戶(hù)的語(yǔ)句啟下在結(jié)構(gòu)上為雙向綁定等模塊的編寫(xiě)打基礎(chǔ)最終效果圖一模板頁(yè) ( 第二篇 )仿寫(xiě)Vue生態(tài)系列___模板小故事. 本次任務(wù) 承上: 完成第一篇未完成的熱更新配置. 核心: 完成模板解析模塊的相關(guān)編寫(xiě), ...
摘要:相等操作符會(huì)有一個(gè)隱形的轉(zhuǎn)換,這個(gè)隱形的轉(zhuǎn)化會(huì)導(dǎo)致結(jié)果很奇怪。 [0] == true; // false [] == ![]; // true 相等操作符會(huì)有一個(gè)隱形的轉(zhuǎn)換,這個(gè)隱形的轉(zhuǎn)化會(huì)導(dǎo)致結(jié)果很奇怪。下面是隱形轉(zhuǎn)換的基本規(guī)則: 其中一個(gè)值是boolean值:兩個(gè)值都轉(zhuǎn)為數(shù)字,false轉(zhuǎn)為0,true轉(zhuǎn)為1 其中一個(gè)值是字符串,另一個(gè)是數(shù)字:都轉(zhuǎn)為數(shù)字再對(duì)比 其中一個(gè)是...
摘要:比如參數(shù)表示使用我們通常使用的十進(jìn)制數(shù)值系統(tǒng)。始終指定此參數(shù)可以消除閱讀該代碼時(shí)的困惑并且保證轉(zhuǎn)換結(jié)果可預(yù)測(cè)。當(dāng)未指定基數(shù)時(shí),不同的實(shí)現(xiàn)會(huì)產(chǎn)生不同的結(jié)果,通常將值默認(rèn)為。 showImg(https://segmentfault.com/img/bVbvtHZ?w=536&h=116); 為什么是[1,NaN,NaN]而不是[1,2,3]? 首先看下 Array.map()函數(shù)在MD...
閱讀 3682·2021-09-27 14:02
閱讀 1796·2019-08-30 15:56
閱讀 1749·2019-08-29 18:44
閱讀 3285·2019-08-29 17:21
閱讀 492·2019-08-26 17:15
閱讀 1180·2019-08-26 13:57
閱讀 1248·2019-08-26 13:56
閱讀 2889·2019-08-26 11:30