慕沐林林
这是一种生成树的非常简单的方法,但理想情况下,您需要了解JavaScript中的指针/引用才能了解它是如何工作的。(它确实创建了很多指针/引用,还有其他方法可以用更少的指针和更少的内存使用量来做到这一点。此外,这仅在对平面列表进行排序并且确实会改变原始列表时才有效。还有其他方法可以从平面数组生成树,使用递归等,并且不会改变列表)重要的是,基元(数字、布尔值等)存储在堆栈上,对象(如数组、对象等)存储在堆上。对象的变量只是指向堆上对象位置的指针。我不确定推荐什么文章或YouTube视频,因为我阅读/观看了这么多不同的文章或视频 - https://www.google.com/search?q=stack+vs+heap坚持你的例子,让我试着逐行解释(在代码中加上注释):let a = [ { id: 1, name: "ADMINISTRATION" }, { id: 2, name: "ADMIN_GROUP", parentId: 1 }, { id: 3, name: "ADMIN_GROUP_CREATE", parentId: 2 }, { id:168, name: "HELP_EDIT" }]function makeTreeFromArray(list) { const treeArray = []; const treeMap = {}; //this loop generates the treeMap //and adds a .children prop to each item in the list: list.forEach((item, index) => { treeMap[item.id] = index; // list[index].children = []; item.children = []; }); //Output of that function: //it maps ids to index in the list /* treeMap = { "1": 0, "2": 1, "3": 2, "168": 3 } */ //this loop pushes each child to its parent in the original list (if it has a parentId) //and it does this using the treeMap (essentially a lookup of ids to indexes). //if the child does not have a parent, then it is the highest level parent //in this case, push it to our output treeArray (with all its children): list.forEach(item => { if (item.parentId) { list[treeMap[item.parentId]].children.push(item); } else { treeArray.push(item); } }); //console.log('treeMap', treeMap); //console.log('list', list); return treeArray;}const newArr = makeTreeFromArray(a);console.log(newArr);它之所以有效,是因为如果创建指针/引用(原始指针的副本),则可以通过原始指针或新副本更新原始项目。下面是一个微不足道的例子:了解如何使用任一变量更改原始数据(同样重要的是,我们正在使用对象,使用基元,这将是不一样的)。(注意:JavaScript 是垃圾回收的,因此每当堆上指向内存的所有指针都超出范围时,就会清理数据。默认情况下,JavaScript使处理内存变得“简单”,但是随着您更深入地了解它正在做的事情,它变得更加复杂。像C++或Rust这样的低级语言让你自己管理内存 - 但Rust通过编译器帮助你做到这一点,C++你基本上是独立的,可能会导致问题)//original is a pointer to a location on the heap, where the object lives:const original = {id: 1, children: []};//pointerToOriginal is just a copy of the original pointer,//so now they both point to the same location on the heap:const pointerToOriginal = original;//I can use either pointer, to change the object on the heap:pointerToOriginal.id +=1;//and it can be seen with both pointers (as they point to the same object):console.log(pointerToOriginal, original);//I can use either pointer, to change the object on the heap:original.id +=1;//and it can be seen with both pointers (as they point to the same object):console.log(pointerToOriginal, original);//Finally, we can see that the objects are equal//and by default JavaScript just compares the memory addresses of each pointer:console.log(pointerToOriginal == original); //true如果你想制作真正的副本,你需要这样做。对于只有基元数据的“平面”对象,您可以像这样执行此操作(使用散布运算符)。对于嵌套对象和/或以 Object 作为其值的对象,这不会完全开箱即用,因为它只会创建一个“浅层副本”,但我们可以创建一个深度复制函数(但这是一个很长的讨论)://original is a pointer to a location on the heap, where the object lives:const original = {id: 1, children: []};//pointerToOriginal is now a new pointer to a shallow copy of the original object,//so now they both point to DIFFERENT locations on the heap:const pointerToNewCopy = {...original};//Each pointer now only updates its Object on the heap:pointerToNewCopy.id +=1;//we see the orignal is un-changed:console.log(pointerToNewCopy, original);//Each pointer now only updates its Object on the heap:original.id +=1;//we see the pointerToNewCopy is un-changed:console.log(pointerToNewCopy, original);//Finally, we can see that the objects are not equal (even though all props are the same)//this is because by default JavaScript just compares the memory addresses of each pointer:console.log(pointerToNewCopy == original); //false重要的是,我们使用上面的对象,使用基元,它将是不一样的://primitive example:let original = 5;let copy = original;//only the copy changes:copy +=1;console.log(copy, original);//only the original changes:original +=1;console.log(copy, original);如果您从函数中 return 语句之前的最后一行(或者您可以在函数运行后向外调用它,因为它改变了原始列表),您将看到以下内容(在此代码段的控制台中,Google chrome 控制台不会通知您有指针/引用副本)。需要注意的重要事项是,id=1 的子级是该列表中原始项(ref4 和 ref6)的引用副本:console.log(list)makeTreeFromArray(list)这就是树的构建方式,从这个突变的原始列表中。希望现在有意义吗?这是一个不平凡的话题。list = [ { "id": 1, "name": "ADMINISTRATION", "children": [ { /**id:4**/ "id": 2, "name": "ADMIN_GROUP", "parentId": 1, "children": [ { /**id:6**/ "id": 3, "name": "ADMIN_GROUP_CREATE", "parentId": 2, "children": [] } ] } ] }, /**ref:4**/, /**ref:6**/, { "id": 168, "name": "HELP_EDIT", "children": [] }]