前段时间接手一个老项目(jQuery+React混在一块)的迁移工做,除了用React
重写一些老页面(这种至少是本身写的,内心有数,调起来也省心),剩下的就是将原有的React迁到新仓库里,复制!粘贴!改路径!完事!是个体力活。javascript
你觉得这样就结束了?一大堆bug在等着,整整改了半个月,虽然都是些小问题,但成天被测试钉来钉去仍是蛮头疼的。java
如今来讲说今天的主角吧,这是一个不起眼的问题,最初发现的时候很让人摸不着头脑。node
part one数组
const res = request(url);
console.log(res); // { stat: 'ok', data: { value: 1, childs: [1, 2, 3] } }
复制代码
上面的代码很普通,可是打开控制台的Network去看这个接口的返回结果是:{ stat: 'ok', data: { value: 1, children: [1, 2, 3] } }
,你没有看错,返回的数据中是children
,而console.log
打印出来的是childs
,是否是很难以想象?接下来咱们逐一排查问题。浏览器
part twobash
const res = request(url);
console.log(JSON.stringify(res)); // { stat: 'ok', data: { value: 1, children: [1, 2, 3] } }
console.log(res); // { stat: 'ok', data: { value: 1, childs: [1, 2, 3] } }
复制代码
这里咱们把res
转成字符串再输出,和直接输出对比发现转成字符串后的结果是和接口返回的结果同样的,都是children
。看到这里相信有很多小伙伴已经清楚是怎么回事了,但应该也有部分同窗脑子里全是问号了(好比当时的我)。 异步
res
,那就顺藤摸瓜,找到使用他的地方。而后就发现了这段代码:
res.data.children = res.data.childs;
delete res.data.childs;
复制代码
原来是为了换字段,直接操做了源数据,而对象,数组都是引用类型的,so...性能
那难道console.log
难道是异步执行的吗,否则代码顺序执行的话前面输出的结果应该是对的呀?测试
这么说其实不许确,咱们把一样的代码换个方式执行,好比node.js:ui
而后跟在浏览器中执行对比一下:
这样就很清晰了,问题在于浏览器控制台的输出机制,但为何要这么设计呢?在网上查阅了一番后,得出的结论是:若是顺序执行console.log(输出的内容较大,层级较深)
,可能会形成阻塞,影响用户体验,因此浏览器在后台异步处理了console.log
。
在某些条件下,某些浏览器的console.log(..) 并不会把传入的内容当即输出。出现这种状况的主要缘由是,在许多程序(不仅是JavaScript)中,I/O 是很是低速的阻塞部分。因此,(从页面/UI 的角度来讲)浏览器在后台异步处理控制台I/O 可以提升性能,这时用户甚至可能根本意识不到其发生。 ---《你不知道的javascript》
而经过JSON.stringify
转成字符串再输出至关于给res
拍了一次快照(记录了他最初的样子),后面再操做该对象也不会影响到这个字符串(你操做对象关我字符串什么事)。
问题搞清楚了,下面要考虑的就是怎么解决以及之后如何避免。
归根到底就是一句话:
不要操做源数据!
不要操做源数据!
不要操做源数据!
使用以前深拷贝一下,可使用最简单粗暴的JSON.parse(JSON.stringify(res))
。
鸡汤:坑踩多了没关系,反正后面还多着呢