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