JavaScript遗留代码重构:从A到Z

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么遗留代码不是死刑

每个开发者迟早都会遇到遗留的 JavaScript 代码。可能是 5 年前"随手"编写的单体 Node.js 脚本,或是连高级开发人员都不敢触碰的混乱回调函数。然而,重构遗留代码不仅仅是"从头重写所有内容",而是一项在不改变外部行为的前提下改善架构的系统性工作。

在本文中,我们将详细讲解 JavaScript(Node.js)的分步重构计划:从分析和测试到实现现代模式。您将学习如何使用实际示例,安全地将"烂摊子"转变为可维护的代码。



1. 遗留代码的分析与映射

在修改代码之前,需要了解其当前状态。首先创建依赖关系图并识别"热点区域"。



1.1. 分析工具

对于 JavaScript/Node.js,使用以下工具:

  • ESLint 配合 complexity 和 max-lines 规则——用于识别复杂函数。
  • Plato 或 CodeClimate——用于可视化圈复杂度。
  • Madge——用于构建模块依赖关系图。


1.2. 识别"代码坏味"

典型的遗留 JS 问题:

  • 超过 100 行的函数。
  • 全局变量和状态突变。
  • 回调地狱(嵌套的异步调用)。
  • 缺乏模块化(单个文件超过 2000 行)。


// "代码坏味"示例:一个包揽所有事情的函数function processUserData(userId) {    // 100 行:验证、数据库查询、格式化、发送邮件    // ... 难以测试和维护}


2. 构建"安全网":测试

没有测试,重构就是"俄罗斯轮盘赌"。创建表征测试(捕捉当前行为的测试)。



2.1. 添加单元测试

使用 Jest 或 Mocha。从关键路径开始:



// 测试现有函数,不修改它const { processUserData } = require('./legacy');

describe('processUserData (遗留)', () => { it('应处理 ID 为 1 的用户', () => { const result = processUserData(1); expect(result).toHaveProperty('email'); expect(result.status).toBe('processed'); });});


2.2. Node.js 的集成测试

如果代码与数据库或 API 交互,使用 supertest 进行 HTTP 请求:



const request = require('supertest');const app = require('../app');

describe('POST /api/users', () => { it('应创建用户(遗留行为)', async () => { const res = await request(app) .post('/api/users') .send({ name: 'Test' }); expect(res.status).toBe(200); expect(res.body.id).toBeDefined(); });});


3. 逐步重构:从单体到模块

我们将过程分为 3 个阶段:隔离、分解、优化。



3.1. 隔离与包装

不要触及内部实现——将遗留函数包装在新的接口中:



// 遗留函数function oldFormatDate(date) {    // ... 50 行包含突变的代码    return formattedDate;}

// 新包装器function formatDateSafe(date) { if (!(date instanceof Date)) { throw new Error('无效日期'); } return oldFormatDate(date);}


3.2. 使用提取函数进行分解

将逻辑块提取为独立的函数:



// 之前:一个 80 行的函数function handleOrder(order) {    // 1. 验证    if (!order.id || !order.total) { ... }    // 2. 折扣计算

博客

书籍推荐