每个程序员必备的重构技巧

推荐阅读:如何写出无法维护的代码

这里的重构技巧是:如何让代码更可读,而不是使代码性能更高

一、代码优化基本原则

  1. 易读性优先

    尤其是在前端开发领域,你的代码好读比你的代码执行得快是更重要的

  2. 如果不是性能瓶颈,就不要为了性能而改写代码

  3. 复杂性守恒原则:无论你怎么写代码,复杂性都是不会消失的

    推论:如果逻辑很复杂,那么代码看起来就应该是复杂的;如果逻辑很简单,代码看起来就应该是简单的。

二、如何命名

1. 程序员三大难题

  1. 变量命名
  2. 缓存失效
  3. 循环边界

2. 命名原则

  1. 注意词性
  • 普通变量/属性用「名词」
js 复制代码
const person = {
  name: 'Leo',
}
  • bool 变量/属性用 形容词be 动词情态动词 或者 hasXxx

    js 复制代码
    const person = {
      dead: false, // 如果是形容词,前面就没必要加 is,比如isDead 就很废话
      canSpeak: true, //情态动词有 can、should、will、need 等,情态动词后面接动词
      isVip: true, // be 动词有 is、was 等,后面一般接名词
      hasChildren: true, // has 加名词
    }
  • 普通函数/方法用动词开头

    js 复制代码
    var person = {
      run() {}, // 不及物动词
      drinkWater() {}, // 及物动词
      eat(foo) {}, // 及物动词加参数(参数是名词)
    }
  • 回调、钩子函数用介词开头,或用动词的现在完成时态

    js 复制代码
    const person = {
      beforeDie() {},
      afterDie() {},
      // 或者
      willDie() {},
      dead() {}, // 这里跟 bool 冲突,你只要不同时暴露 bool dead 和函数 dead 就行,怕冲突就用上面的 afterDie
    }
    button.addEventListener('click', onButtonClick)
    const component = {
      beforeCreate() {},
      created() {},
      beforeMount() {},
      mounted() {},
      beforeUpdate() {},
      updated() {},
      activated() {},
      deactivated() {},
      beforeDestroy() {},
      destroyed() {},
      errorCaptured() {},
    }
  • 容易混淆的地方加前缀

    js 复制代码
    div1.classList.add('active') // DOM 对象
    div2.addClass('active') // jQuery 对象
    // 建议改成
    domDiv1
    // 或
    elDiv1.classList.add('active')
    $div2.addClass('active')
  • 属性访问器函数可以用名词

    约定俗成

js 复制代码
$div.text() // 其实是 $div.getText()
$div.text('hi') // 其实是 $div.setText('hi')
  1. 注意一致性
  • 介词一致性

    如果你使用了 before + after,那么就在代码的所有地方都坚持使用 如果你使用了 before + 完成时,那么就坚持使用 如果你改来改去,就「不一致」了,不一致将导致「不可预测」

  • 顺序一致性

    比如 updateContainerWidthupdateHeightOfContainer 的顺序就令人很别扭,同样会引发 「不可预测」

  • 表里一致性

    函数名必须完美体现函数的功能,既不能多也不能少。

    比如

    js 复制代码
    function getSongs(){
      return $.get('/songs').then((response){
          div.innerText = response.songs
      })
    }

    就违背了表里一致性,getSongs 表示获取歌曲,并没有暗示这个函数会更新页面,但是实际 上函数更新了 div,这就是表里不一

    正确的写法是:

    • 要么纠正函数名
    js 复制代码
    function getSongsAndUpdateDiv(){
      return $.get('/songs').then((response){
          div.innerText = response.songs
      })
    }
    • 要么写成两个函数
    js 复制代码
    function getSongs() {
      return $.get('/songs')
    }
    function updateDiv(songs) {
      div.innerText = response.songs
    }
    getSongs().then(response => {
      updateDiv(response.songs)
    })
    • 时间一致性

      添加代码很容易,而改代码是很难的,改名字很容易引发其他 bug

    有可能随着代码的变迁,一个变量的含义已经不同于它一开始的含义了,这个时候你需要及时 改掉这个变量的名字。

    这一条是最难做到的,因为写代码容易,改代码难。如果这个代码组织得不好,很可能会出现牵一发而动全身的情况(如全局变量就很难改)

三、如何改代码

如果你的代码有单元测试,那么改起来就很放心。如果没有单元测试,就需要用小步快跑的策略来修改。

什么是小步快跑:每次不要改很多,每次只改一点点,测试通过后,再改一点点,再测试,再改一点点... 如此反复。

那么如何修改一点点呢?《重构》这本书介绍了很多方法,但是讲得太宏观太啰嗦了,如果你有时间可以看看。

我比较喜欢下面两个经久不衰的方法

1. 使用函数来改代码

步骤:

  1. 将一坨代码放到一个函数里
  2. 将代码依赖的外部变量作为参数
  3. 将代码的输出作为函数的返回值
  4. 给函数取一个合适的名字
  5. 调用这个函数并传入参数
  6. 这个函数里的代码如果超过 5 行或者多处结构相似,则依然有优化的空间,请回到第 1 步

2. 使用对象来改代码

如果使用了函数改造法改造后,发现有太多的小函数,则可以使用对象讲这个函数串起来。

由于 JS 的 this,这种方法可能会不安全

3. 一些固定的套路

  1. 表驱动编程(《代码大全》里说的) 所有一一对应的关系都可以用表来做

    js 复制代码
    // 计算分数对应的等级
    
    // 优化前
    function calculateGrade(score) {
      if (score == 100) {
        return 'A+'
      } else if (score >= 90) {
        return 'A'
      } else if (score >= 80) {
        return 'B'
      } else if (score >= 70) {
        return 'C'
      } else if (score >= 60) {
        return 'D'
      } else {
        return 'E'
      }
    }
    
    // 优化后
    function calculateGrade(score) {
      const gradeMap = {
        100: 'A+',
        90: 'A',
        80: 'B',
        70: 'C',
        60: 'D',
        others: 'E',
      }
      const formatScore = parseInt(score / 10, 10)
      return gradeMap[formatScore] || gradeMap['others']
    }
  2. 自说明代码(以 API 参数为例)

    把一个变量名命名好,就已经是自说明代码了

    这里另外一种方式是:把别人关心的东西放在显眼的位置

    js 复制代码
    // 比如你封装一个 Dialog 组件,你可以让开发者知道有这些 API
    class Dialog {
      constructor(options) {
        const defaultOptions = {
          title: '提示',
          content: '<h2>请填写content</h2>',
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          onConfirm: () => {},
          onCancel: () => {},
        }
      }
      this.options = Object.assign({}, defaultOptions, options)
      // Object.assign 就是把对象后面的值依次赋值(覆盖)到前面
    }

    defaultOptions 就类似文档说明

四、什么是 bad smell(坏味道)

有些代码可以用,但是散发着恶臭的味道。

例如:

  1. 表里不一的代码
  2. 过时的注释
  3. 逻辑很简单,但是看起来很复杂的代码
  4. 重复的代码
  5. 相似的代码
  6. 总是一起出现的代码

五、普遍现象 ------ 破窗效应

一种犯罪心理学理论,此理论认为环境中的不良现象如果被放任存在,会诱使人们仿效,甚至变本加厉。

维基百科的例子:

以一幢有少许破窗的建筑为例,如果那些窗没修理好,可能将会有破坏者破坏更多的窗户。最终他们甚至会闯入建筑内,如果发现无人居住,也许就在那里占领、定居或者纵火。又或想像一条人行道有些许纸屑,如果无人清理,不久后就会有更多垃圾,最终人们会视为理所当然地将垃圾顺手丢弃在地上。因此破窗理论强调着力打击轻微罪行有助减少更严重罪案,甚至应该以零容忍的态度面对罪案。

在一个项目中,有一处写得不好的代码,就是破窗效应的起源,最终变为屎山

所以我们程序员要有一点追求:只要是经过你手里的代码,都会比之前好一点。


感谢阅读,下次见 :)

相关推荐
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60619 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅10 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅10 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment10 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax