记一次由js对象赋值机制引起的bug

发布于 2024-04-23  824 次阅读


最近有朋友给PicList提issue发现了一个比较奇怪的bug,[Bug]: AWS S3上传文件后 获取的url有问题 · Issue #192 · Kuingsmile/PicList · GitHub,简单的来说,就是在桌面版的PicList中,每上传一次,都会在自定义域名后额外增加一个桶名。经过我的测试,发现确实有这个情况,但奇怪的是用命令行上传时又没有问题。

经过排查和定位,怀疑到了这一行代码:

const userConfig: IAwsS3PListUserConfig = ctx.getConfig("picBed.aws-s3-plist")
if (userConfig.urlPrefix) {
    userConfig.urlPrefix = userConfig.urlPrefix.replace(/\/?$/, "")
    if (userConfig.pathStyleAccess && !userConfig.disableBucketPrefixToURL) {
    userConfig.urlPrefix += "/" + userConfig.bucketName
    }
}

从代码来看,只有在这里才对自定义域名添加了桶名后缀,但为什么会每上传一次就多一个桶名而不是清空的呢,继续去看ctx对应的PicGo类中getConfig函数的实现:

getConfig<T> (name?: string): T {
    if (!name) {
      return this._config as unknown as T
    } else {
      return get(this._config, name)
    }
  }

结合到JavaScript的对象赋值的机制,可以发现当getConfig函数获取到的是一个对象的时候,对userConfig变量进行赋值后,实际上userConfig变量保存的是对PicGo实例的this._config.name对象的同一个引用。因此,直接通过userConfig.urlPrefix += "/" + userConfig.bucketName修改urlPrefix属性,就会反映到实例的this._config对象里的对应属性中。

但还有问题,为什么直接用命令行上传就没有问题,只有使用桌面版才有问题呢?经过梳理,发现是由于在上传时,桌面版每次都传递了相同的PicGo实例,因此上一次对this._config的修改会影响到下一次的上传,而命令行上传时,每次都会新new一个PicGo实例出来,就不会互相影响。

定位了问题后,修复起来就简单了,将userConfig.urlPrefix保存的字符串值先赋值给一个新的变量再操作,就不会影响到PicGo实例了。