JavaScript代码优化技巧示例详解
代码不仅要能被机器执行,更要能被人理解。本文总结了15个源自经典重构理论的JavaScript代码优化技巧,包括提炼函数、策略模式应用及条件表达式拆解等。通过直观的“重构前”与“重构后”代码对比,深入解析如何消除重复逻辑、降低耦合度并提升代码清晰度。
核心优化原则与目的
代码的本质是传递思想和记录信息,因此“写得清楚”比“跑得通”更为关键。通过对既有代码进行结构化调整(重构),可以在不改变外部行为的前提下改善其内部设计。以下是实战中常用的具体优化手段:
提取与简化逻辑结构
第1步:提炼函数
操作目的:将一段晦涩的、需要仔细阅读才能理解的代码块,独立为单一职责的函数,并以该功能命名,实现“见名知意”。
执行动作:
- 识别代码片段:当发现一段代码包含计算、打印或复杂流程时。
- 抽取步骤:将该段代码复制到一个新函数中,原位置调用新函数。
- 命名规范:新函数名应直接描述其行为(如
printBanner)。
// 重构前:内联代码导致阅读困难
function printOwing(invoice) {
let outstanding = 0;
console.log("***********************");
console.log("**** Customer Owes ****");
console.log("***********************");
}
// 重构后:逻辑清晰独立
function printOwing(invoice) {
let outstanding = 0;
printBanner(); // 意图明确
}
function printBanner() {
console.log("***********************");
console.log("**** Customer Owes ****");
console.log("***********************");
}
第2步:函数参数化
操作目的:消除多个逻辑相似但字面量不同的重复函数。
执行动作:合并重复逻辑,将被替换的字面量值转换为 params 参数传入。
// 重构前:存在高度相似的重复代码
clickFaultsItem(item){ this.$u.route({ url:'xxx', params:{ id: item.id, type: '异常' } }) }
clickNormalItem(item){ this.$u.route({ url:'xxx', params:{ id: item.id, type: '正常' } }) }
// 重构后:抽象为通用方法
clickItem(id, type){
this.$u.route({ url:'xxx', params:{ id, type } });
}
第3步:以卫语句取代嵌套
操作目的:减少代码缩进层级,使主流程更突出。适用于罕见条件的提前返回。
执行动作:判断边界或错误条件,满足则直接 return,不再进入深层 if-else 嵌套。
// 重构前:深层嵌套
if(employee.isSeparated) { result = {amount: 0}; } else { ... }
// 重构后:扁平化结构,异常先排除
function payAmount(employee) {
if (employee.isSeparated) return { amount: 0, reasonCode: "SEP" };
if (employee.isRetired) return { amount: 0, reasonCode: "RET" };
return someFinalComputation();
}
第4步:拆分阶段与循环
操作目的:一个循环或函数不要干两件事。拆分后各模块可独立修改,且更易利用现代语法(如 map/reduce)优化。
执行动作:将不同维度的处理(如价格计算与运费计算)拆分为独立函数或循环。
// 拆分后的单职责循环写法
let totalSalary = 0;
for (const p of people) totalSalary += p.salary; // 仅做加法
let youngest = Infinity;
for (const p of people) if (p.age < youngest) youngest = p.age; // 仅找最小值
数据结构与变量的优化
第5步:使用策略模式替换“胖”分支
操作目的:解决过长的 switch-case 或 if-else,使扩展新的业务类型时无需修改主干逻辑。
执行动作:建立常量/对象字典,将不同条件下的处理逻辑作为属性挂在其上。
// 重构后:通过查找表替代大量if判断
const priceHandler = {
newUser(p){ return p > 50.1 ? p - 50 : p },
back(p){ return p > 200 ? p - 50 : p },
activity(p){ return p > 300 ? p - 100 : p }
}
function getPrice(tag, originPrice){ return priceHandler[tag](originPrice); }
第6步:拆分变量
操作目的:避免同一个变量在生命周期内承担完全不同的含义。
执行动作:根据变量的实际用途,将其重命名为更有意义的名字。
// 重构前:temp先后代表周长和面积,极易误用
// const temp = 2 * (height + width); ... temp = height * width;
// 重构后:语义精准
const perimeter = 2 * (height + width);
const area = height * width;
第7步:提炼变量与内联变量
操作目的:
1. 提炼变量:将复杂的数学表达式分解为易懂的中间变量,方便调试。
2. 内联变量:如果变量名没有比表达式本身提供更多信息,直接删除变量,使用原始表达式。
第8步:封装变量
操作目的:当变量作用域超出单个函数时,禁止外部直接访问,必须通过 getter/setter。
执行动作:定义全局作用域的私有变量,并提供专用的访问方法。
function getDefaultOwner() { return defaultOwner; }
function setDefaultOwner(arg) { defaultOwner = arg; }
复杂条件表达式的处理
第9步:分解条件表达式
操作目的:当 if 后面的条件太复杂难以一眼看透时,将其逻辑封装成独立的布尔判断函数。
执行动作:将长条件转为调用 summer() 等自解释函数。
// 将晦涩的条件检查转化为有意义的函数调用
charge = summer() ? summerCharge() : regularCharge();
function summer() { return !date.isBefore(start) && !date.isAfter(end); }
第10步:合并条件表达式
操作目的:当一连串检查虽然条件不同,但最终结果一致(如都返回0)时,使用逻辑或 |> 将它们合并为一个函数。
// 多个独立检查合并为一个判定条件
if (isNotEligableForDisability()) return 0;
function isNotEligableForDisability() {
return seniority < 2 || monthsDisabled > 12 || isPartTime;
}
系统架构层面的分离
第11步:查询与修改分离
操作目的:遵循“纯函数”原则,如果一个函数同时产生返回值又有副作用(如修改全局状态/报警),应当将其拆分。
执行动作:分为 findMiscreant(查)和 alert...(改)两个独立函数。
第12步:引入断言(Assert)
注意:虽然原文未详细展开此节,但在高级优化中,可使用断言工具库在代码入口验证前置条件,增强程序健壮性,若条件失败则抛出异常而非静默崩溃。
第13步:将类复制造成子类的依赖改为组合
操作目的:当一个子类只使用了父类的一小部分功能却继承了全部时,这违背了“最少知识原则”。
执行动作:在子类中持有父类的一个实例(组合),并在子类中直接委托调用父类的特定方法。
第14步:用过程-oriented风格改写面向对象
操作目的:如果系统中所有的数据都只集中在少数几个类中,而大量的业务逻辑分散在各个类的方法里,这种分布式的逻辑会导致巨大的修改成本。
建议:将此类数据集中的逻辑抽离到单独的过程式函数中处理,而非强行塞入各个类的成员方法。
第15步:隔离客户端与修改
操作目的:如果某个系统或第三方组件经常发生变化,而我们自己调用的部分相对稳定,应将两者隔离。
执行动作:创建一个“稳定层”,所有来自不稳定的外部的变动都在这一层消化,不直接影响我们的核心代码。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









