前段时间接手一个老项目 (jQuery+React 混在一块) 的迁移工作, 除了用 React 重写一些 vm+jQuery 的老页面(这种至少是自己写的, 心里有数, 调起来也省心), 剩下的就是将原有的 React 迁到新仓库里, 复制! 粘贴! 改路径! 完事! 是个体力活.
你以为这样就结束了? 一大堆 bug 在等着, 整整改了半个月, 虽然都是些小问题, 但整天被测试钉来钉去还是蛮头疼的.
现在来说说今天的主角吧, 这是一个不起眼的问题, 最初发现的时候很让人摸不着头脑.
- 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 two
- 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:
然后跟在浏览器中执行对比一下:
这样就很清晰了, 我们都知道 JS 需要一个运行环境, 而 console.log 的执行跟运行的环境有关. 而通过 JSON.stringify 转成字符串再输出相当于给 res 拍了一次快照(记录了他最初的样子), 后面再操作该对象也不会影响到这个字符串(你操作对象关我字符串什么事).
问题搞清楚了, 下面要考虑的就是怎么解决以及以后如何避免.
归根到底就是一句话:
不要操作源数据!
不要操作源数据!
不要操作源数据!
使用之前深拷贝一下, 可以使用最简单粗暴的 JSON.parse(JSON.stringify(res)).
鸡汤: 坑踩多了不要紧, 反正后面还多着呢
来源: https://juejin.im/post/5c126088518825566d235f77