12、组织数据(上):拆分变量、字段改名、以查询取代派生变量
数据是代码的血液。我见过太多系统,逻辑写得还行,但数据组织一塌糊涂,最后改个需求要翻遍整个项目。说白了,数据组织不好,代码再“整洁”也是空中楼阁。
这一章我们聚焦三个最基础、也最常用的重构手法:拆分变量、字段改名、以查询取代派生变量。它们看起来简单,但用好了,能让你的代码清晰度提升一个档次。
1. 拆分变量:一个变量只做一件事
你见过那种一个变量被反复赋值、每次含义都不同的代码吗?
let temp = 2 * (height + width);
console.log(temp);
temp = height * width;
console.log(temp);
这个 temp 变量,第一轮是周长,第二轮是面积。我刚开始写代码时也这么干,觉得省变量名很“聪明”。直到有一次我 debug 到凌晨三点,才发现自己把周长当面积传给了下游函数……
正确的做法是:一个变量只承载一个含义。
const perimeter = 2 * (height + width);
console.log(perimeter);
const area = height * width;
console.log(area);
你看,现在每个变量职责清晰,不会混淆。而且 const 一用,连意外修改的可能性都堵死了。
2. 字段改名:名字就是最好的注释
我接手过一个老项目,有个字段叫 a,我以为是“年龄(age)”,结果发现是“地址(address)”。这种命名,简直是给后人挖坑。
字段改名看似简单,但有个坑:改名字不只是改一处。你得确保所有引用点都同步更新。好在现代 IDE 都支持重命名重构,但如果你用的是纯文本编辑器,那就得小心了。
举个例子,一个订单类里有个字段叫 n:
class Order {
constructor(n) {
this.n = n;
}
}
这个 n 是啥?数量?编号?名字?没人知道。我建议改成有业务含义的名字:
class Order {
constructor(customerName) {
this.customerName = customerName;
}
}
改名字时,记得用“迁移式改名法”:先新增字段,逐步替换引用,最后删除旧字段。别一次性改完,尤其是大项目,容易出事。
3. 以查询取代派生变量:能算出来的就别存
这是我最喜欢的一个重构手法。很多新手喜欢把能推导出来的数据也存成字段,结果导致数据不一致。
比如,一个订单类里同时存了 items(商品列表)和 totalPrice(总价):
class Order {
constructor(items) {
this.items = items;
this.totalPrice = this.calculateTotal();
}
calculateTotal() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
}
问题来了:如果有人直接修改了 items 数组,但忘了更新 totalPrice,数据就错了。我曾在项目中遇到过这种 bug,排查了两天才发现是某个同事在业务代码里直接 push 了商品,没调计算总价的方法。
解决方案很简单:把派生数据变成查询方法。
class Order {
constructor(items) {
this.items = items;
}
get totalPrice() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
}
现在 totalPrice 是个 getter,每次访问都实时计算。数据永远一致,而且你少维护一个字段。
知识体系图
下面这张图总结了本章三个重构手法的核心逻辑和适用场景:
避坑指南
- 拆分变量时别过度:如果一个变量只在循环里用,且每次赋值含义相同(比如累加器),那就不需要拆。
- 字段改名要同步文档:我曾经改了一个字段名,但忘了更新接口文档,结果前端同事对接时一脸懵。改完名记得通知相关方。
- 派生变量不是都不能存:如果计算开销极大(比如全表扫描),或者需要历史快照,那还是得存。但这种情况很少见。
好了,这一章就到这里。数据组织是代码整洁的基石,这三个手法虽然基础,但用好了能解决很多实际问题。下一章我们继续聊组织数据的其他手法,到时候见。