你的代码能运行——但这并不意味着它很好
\n\n你写了一个脚本。它能运行。客户很满意。但一个月过去了,你惊恐地看着那个已经膨胀到200行的函数。里面混杂着API请求、DOM操作和一些关于日期的魔法。熟悉吗?
\n\n欢迎来到这样一个世界,在这里JavaScript代码重构不是完美主义者的任性,而是生存的必需。如果你希望你的项目能够长久存活,并且在第一次修改时不会崩溃,这篇文章就是为你准备的。
\n\n什么是重构,什么不是
\n\n我们从最重要的开始。JavaScript代码重构是在不改变程序外部行为的情况下改变其内部结构的过程。你并没有添加新功能,也没有修复bug。你只是让代码更干净、更易理解、更易于维护。
\n\n很多人把重构和重写混为一谈。这是两回事。重写是指你扔掉旧代码,从头开始写新的。重构是进化,而不是革命。你通过小步骤改变代码,每次都检查是否有什么东西被破坏了。
\n\n何时需要进行JavaScript重构?
\n\n以下是几个明确的迹象,表明你的代码在呼救:
\n\n- \n
- 三次法则。如果你第三次复制同一段代码——是时候把它提取到一个单独的函数中了。 \n
- 怪物函数。函数超过30-40行,无法一眼读完。 \n
- “魔法数字”。代码中散布着像
86400000或42这样的数字,没有注释和常量。 \n - 全局变量。如果你害怕修改一个变量,因为不知道它还在哪里被使用。 \n
- 测试。如果你害怕进行修改,因为没有自动化测试——这是第一个警钟。 \n
但也有不需要重构的情况:在截止日期前、在没有测试的生产环境中、或者代码是为明天就要丢弃的原型临时拼凑的。
\n\nJavaScript重构技术:从简单到复杂
\n\n1. 提取函数 (Extract Function)
\n\n最流行且最安全的技术。你找到可以分组的代码片段,并将其提取到一个单独的函数中。
\n\n// 重构前\nfunction calculateInvoice(items) {\n let total = 0;\n for (let item of items) {\n total += item.price * item.quantity;\n }\n let discount = 0;\n if (total > 1000) {\n discount = total * 0.1;\n }\n return total - discount;\n}\n\n// 重构后\nfunction calculateSubtotal(items) {\n return items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n}\n\nfunction calculateDiscount(total) {\n return total > 1000 ? total * 0.1 : 0;\n}\n\nfunction calculateInvoice(items) {\n const subtotal = calculateSubtotal(items);\n const discount = calculateDiscount(subtotal);\n return subtotal - discount;\n}\n\n\n发生了什么变化?现在每个函数只负责一个动作。你可以在其他地方重用 calculateSubtotal,而 calculateDiscount 也很容易单独测试。
2. 用常量替换魔法数字
\n\n永远不要在代码中使用“裸”数字。一个月后,你自己都会不记得 86400000 是什么意思。
// 不好\nif (Date.now() - lastLogin > 86400000) {\n // 请求重新登录\n}\n\n// 好\nconst MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000;\nconst ONE_DAY_MS = 86400000; // 或者只是一个可读的常量\n\nif (Date.now() - lastLogin > ONE_DAY_MS) {\n // 请求重新登录\n}\n\n\n3. 简化条件结构
\n\n复杂的嵌套 if-else 是可读性的主要敌人。使用提前返回(early return)和三元运算符。
<