记一次require+闭包的bug检查

来源:互联网 发布:英语四级真题推荐知乎 编辑:程序博客网 时间:2024/05/01 11:21

这里写图片描述

今天的你,写bug了吗?

唉什么都不想说,只怪自己太蠢。闭包和引用,两个最基础的知识,结合到了一起,却忘得一干二净,谨以此文,来记录下这个已经被我K.O的bug,愿来生路上,你不再出现。

情况是这样的,有个函数一个网络请求加处理步骤,结构大概是这样

let result = {}function func(url) {    return new Promise((resolve, reject) => {        request(url, (err, res, body) => {            // 处理result并resolve出去        })    })}module.exports = func

本来觉得写一起好了,然后发现越写越长,于是就把它放到了另一个文件里面,然后主函数来引用。

不自觉为自己点个赞 不自觉为自己点个赞

于是乎,大刀阔斧地把这个函数给分离了出去,主函数的结构是这样

const func = require('./func')let arr = []let result = await func(url)arr.push(unit)

最后来讲arr转换成字符串写入文件,结果这个时候就出现一个问题了,什么问题呢,写完发现文件里面的所有result都是最后一次返回的result,bug就此出现。

这里写图片描述 what happened?

其实聪明的你估计早就已经发现问题在哪儿了,但当是写了一天代码的我,头昏脑胀,完全不知道问题出在哪儿,定位了一圈,发现全出在这个引用的函数里面,然后我便去函数里面找bug,最后终于发现了问题,就出现代码开头的第一句里面,就是下面这句

let result = {}

这句定义了一个对象,然后经由func函数处理并返回,但问题就在这是个对象上面,下面一步一步来解释

result对象定义在func函数的外部,而模块exports出去的是这个函数,这样在主文件里面引用的时候,result共享的都是一个实例,这就是require的特性,也就是说,即使在主文件里面多次调用func函数,这样只要result函数在func函数内部没有改变指向,那么arr里面每次接收到的result都是一个实例,所以也就出现了虽然多次push都是不同的,但是在result改变时,arr里面的所有元素都会跟result同步改变。

其实一个很简单的例子就能体现出这个结果

let a = []let b = {prop: 123}for(let i=0;i<3;i++){    b.prop++    a.push(b)}

这样最后你会发现a的内部所有的元素都会变为{prop: 126}.

回到上面的问题,主函数require进来的func其实相当于一个闭包,而这个闭包引用了result,而且由于require共享一个实例,这样每次改变result,自然arr的元素就会跟着改变了。

解决问题容易,但是如何避免问题,才是我们应该关注的问题。
这还只是个两个文件的程序,一个大型的应用程序可能几百个文件都很正常,这样如果某一个变量定义不慎,出现上面的问题,那么bug定位就比这个要难多了。

好了,今天的bug说就到这里,下期节目再见。

这里写图片描述

写bug去了