前几日同事对树形数据进行处理,发现代码可以按照逻辑运行,但是console获取到的后端数据里,没有被处理的树形数据.
这里简单复现一下问题代码:
let res = await getTreeMenuList()
console.log(res)
const { menuList } = res.data
generatorRoutes(menuList: any) {
let routes: any = []
const deepList = (list: any) => {
while (list.length) {
let item = list.pop()
if (item.children && !item.action) {
deepList(item.children)
}
}
}
deepList(menuList)
return routes
}
generatorRoutes(menuList)
以上的代码在两次console的地方都是显示有东西,但是实际点开的时候,menuList的length为0
如果你只想知道简单原因和解决方法:
因为函数处理的逻辑里给list.pop(),且没有深拷贝切断联系.
因为对象是引用类型,所以此时的操作改变了.menuList,
所以会发现,控制台打印的时候显示的是有数组内容,但是点开会发现length:0,且没有内容.
所以可知:切断联系或者不修改堆内存就好了.(深拷贝或者不对数组进行pop()之类的改变操作即可)
上面已经简单说明了问题出现的原因,接下来稍微深入一点.
因为操作的还是同一个堆内存,所以控制台点开,打印的是处理之后数据内容,因为递归pop()了所以length是0
那么,为什么呢?
很简单,其实console.log()在控制台输出引用值时确实是当时的值,但是你点开箭头的时候它会重新获取这些引用的值
这里写一个简单的示例代码:
let test = {a:{b:1}}
console.log(test.a)
test.a.b = 2
显示的是{b:1},但是点开是b:2.
这就是因为在console.log的时候快照拍到的是{b:1}栈内存,但是因为下面的代码修改了堆内存,所以重新获取引用的值:{b:2}
当然,我也看到有人说是因为console异步操作,因为:
let test = { a: { b: 1 } }
Promise.resolve().then(() => { console.log(test.a) })
setTimeout(() => { console.log(test.a) }, 0)
test.a.b = 2
不管是宏任务还是微任务打印的出来的结果点开和直接看到的都是{b:2}.
但是!这实际上是因为同步代码执行完了才执行的微任务,然后宏任务,所以此时此刻的test.a不管是当时的值还是重新获取的引用值其实都是{b:2};了.这和同步异步是没有关系的.
let test = { a: { b: 1 } }
console.log(test.a)
test.a = { b: 2 }
以上的代码会出现不管是当时,还是重新获取都是{b:1}, 原因其实很简单,因为 {b: 2} 是复杂类型,堆内存,a的堆内存都换了,所以按照console时的栈内存指向,其实还是{b:1}所在的堆内存,而不是新的堆内存{b:2}
let test = { a: { b: 1 } }
console.log(test.a.b)
test.a.b = 2
以上代码直接打印1,不会改变,因为,test.a.b就是栈内存,没有指向的堆内存.他就是简单类型.也不需要重新获取引用的值…
深拷贝切断引用,不修改原对象都可.
如果不影响代码的逻辑当然也可以不改.(其实断点的时候也是可以看到正确的打印.)