11、搬移特性(下):以函数调用取代内联代码、移动语句、拆分循环、以管道取代循环
好,咱们接着聊搬移特性。上一章讲了搬移函数和搬移字段,这次我把剩下的几个手法一并说完。这几个手法看起来小,但实际用起来特别顺手。我自己的经验是,它们就像代码里的“微整形”,不动大手术,但能让代码顺眼很多。
以函数调用取代内联代码
什么叫内联代码?说白了,就是一段逻辑直接写在调用处,没有封装成函数。比如你看到一段代码,明明在好几个地方都做了类似的事情,但每次都是手写一遍。嗯,这时候就该把它抽出来。
我举个例子。假设你有一段代码,用来计算订单的折扣:
let basePrice = quantity * itemPrice;
let discount = Math.max(0, quantity - 100) * 0.05;
let finalPrice = basePrice - discount;
这段逻辑如果出现在多个地方,每次都要重复写一遍。你想想看,万一哪天折扣规则变了,你得改多少个地方?
正确的做法是把它封装成一个函数:
function calculateDiscount(quantity, itemPrice) {
let basePrice = quantity * itemPrice;
let discount = Math.max(0, quantity - 100) * 0.05;
return basePrice - discount;
}
然后调用处就变成:
let finalPrice = calculateDiscount(quantity, itemPrice);
我在项目中遇到过一种情况:有人把一段 50 行的内联代码复制粘贴了 5 次,每次只改了一个变量名。后来需求变了,改代码的人差点崩溃。所以,别偷懒,该抽就抽。
移动语句
移动语句,听起来很简单,就是把代码行换个位置。但你别小看它,很多时候代码的可读性差,就是因为语句放错了地方。
举个例子:
let result = [];
let items = getItems();
let discount = 0.1;
for (let item of items) {
// 处理每个 item
}
let taxRate = 0.05;
你看,discount 和 taxRate 这两个变量,一个放在循环前面,一个放在循环后面。读代码的人得来回跳着看。更好的做法是把相关的变量放在一起:
let items = getItems();
let discount = 0.1;
let taxRate = 0.05;
let result = [];
for (let item of items) {
// 处理每个 item
}
移动语句的核心原则是:让代码的阅读顺序和逻辑顺序一致。你想想看,读代码就像读故事,谁愿意看一段跳来跳去的情节?
拆分循环
这个手法我特别喜欢。很多程序员喜欢在一个循环里做所有事情,觉得这样性能好。但问题是,代码的可读性会变得很差。
比如这个循环:
let totalSalary = 0;
let youngestAge = Infinity;
for (let person of people) {
totalSalary += person.salary;
if (person.age < youngestAge) {
youngestAge = person.age;
}
}
一个循环做了两件事:计算总薪资和找最年轻的人。读代码的人得同时理解两个逻辑。拆开之后:
let totalSalary = 0;
for (let person of people) {
totalSalary += person.salary;
}
let youngestAge = Infinity;
for (let person of people) {
if (person.age < youngestAge) {
youngestAge = person.age;
}
}
有人会说:“这样性能不就差了吗?要遍历两次数组。” 嗯,这个问题我遇到过很多次。我的回答是:先让代码清晰,再考虑性能。大多数情况下,两次遍历的性能差异微乎其微。如果真的成了瓶颈,再优化也不迟。
以管道取代循环
这个手法是函数式编程的体现。说白了,就是用 map、filter、reduce 这些管道操作来替代传统的 for 循环。
举个例子,假设你有一个员工列表,要找出薪资超过 5000 的员工姓名:
let result = [];
for (let employee of employees) {
if (employee.salary > 5000) {
result.push(employee.name);
}
}
用管道改写:
let result = employees
.filter(e => e.salary > 5000)
.map(e => e.name);
你看,代码变得简洁多了。而且管道的每一步都清晰表达了意图:先过滤,再映射。
我个人习惯是,只要循环里只有简单的过滤和转换,就优先用管道。但要注意,如果循环里有复杂的副作用(比如修改外部变量),那还是用 for 循环更合适。
知识体系总览
下面这张图总结了本章的四个手法,以及它们之间的关系:
总结
这四个手法,说白了就是让你在写代码的时候多问自己几个问题:
- 这段逻辑是不是应该封装成一个函数?
- 这些语句放在这里是不是最合适的位置?
- 这个循环是不是做了太多事情?
- 能不能用更简洁的管道来表达?
我自己的经验是,养成这些习惯之后,代码质量会有明显的提升。而且你会发现,改代码的时候心情也会好很多——因为代码本身就在告诉你它想变成什么样子。
嗯,这一章就到这里。记住,代码整洁不是一蹴而就的,而是靠这些小手法一点一点打磨出来的。