本文来自《Clean Code Applied to JavaScript — Part III. Functions》,讨论了编写整洁代码的基本技巧和建议,尤其是关注可复用的代码单元——函数。虽然示例代码使用的是 JavaScript,但这些最佳实践适用于任何编程语言,包括接近硬件层面的语言。
引言
许多人认为整洁代码的原则仅适用于某些编程语言,尤其是那些高级语言。然而,无论使用哪种语言,这些实践都是值得推荐的。只有通过实际行动,我们才能改变现状。以下是一些具体的实践建议,帮助我们在开发中编写更加整洁和高效的代码。
使用默认参数替代短路操作或条件赋值
大多数编程语言都支持为函数参数设置默认值。这使得我们可以避免使用短路操作和条件赋值。
// 使用短路操作
function setName(name) {
const newName = name || 'Juan Palomo';
}
// 使用默认参数
function setName(name = 'Juan Palomo') {
// ...
}
减少函数参数(理想情况下不多于 2 个)
减少函数参数的个数可以显著提高代码质量。理想情况下,函数参数不应超过 2 个。如果参数过多,可以将它们组合成一个对象。
// 参数较多的函数
function newBurger(name, price, ingredients, vegan) {
// ...
}
// 将参数组合成一个对象
function newBurger(burger) {
// ...
}
// 使用对象解构
function newBurger({ name, price, ingredients, vegan }) {
// ...
}
const burger = {
name: 'Chicken',
price: 1.25,
ingredients: ['chicken'],
vegan: false,
};
newBurger(burger);
避免副作用 - 全局变量
副作用会增加代码的复杂性和出错的可能性。应该避免在函数中修改全局变量,尽量将变量作为参数传递。
// 有副作用的函数
let fruits = 'Banana Apple';
function splitFruits() {
fruits = fruits.split(' ');
}
splitFruits();
console.log(fruits); // ['Banana', 'Apple']
// 无副作用的函数
function splitFruits(fruits) {
return fruits.split(' ');
}
const fruits = 'Banana Apple';
const newFruits = splitFruits(fruits);
console.log(fruits); // 'Banana Apple'
console.log(newFruits); // ['Banana', 'Apple']
避免副作用 - 可变对象
直接修改对象会导致不可预见的问题。应尽量避免使用可变对象,尤其是在使用 JavaScript 的数组方法时。
// 修改数组本身
const addItemToCart = (cart, item) => {
cart.push({ item, date: Date.now() });
};
// 返回新的数组
const addItemToCart = (cart, item) => {
return [...cart, { item, date: Date.now() }];
};
函数应该只做一件事
每个函数只应专注于执行一个任务。对于较大的功能,可以将其拆分为多个小的功能模块。
// 做多件事的函数
function emailCustomers(customers) {
customers.forEach((customer) => {
const customerRecord = database.find(customer);
if (customerRecord.isActive()) {
email(client);
}
});
}
// 拆分后的函数
function emailActiveCustomers(customers) {
customers
.filter(isActiveCustomer)
.forEach(email);
}
function isActiveCustomer(customer) {
const customerRecord = database.find(customer);
return customerRecord.isActive();
}
函数应只有一个抽象级别
一个函数应只包含一个抽象级别的代码。如果有不同的抽象级别,应该将其拆分成多个函数。
// 包含多个抽象级别的函数
function parseBetterJSAlternative(code) {
const REGEXES = [
// ...
];
const statements = code.split(' ');
const tokens = [];
REGEXES.forEach((REGEX) => {
statements.forEach((statement) => {
tokens.push( /* ... */ );
});
});
const ast = [];
tokens.forEach((token) => {
// lex...
});
ast.forEach((node) => {
// parse...
});
}
// 拆分后的函数
const REGEXES = [ // ...];
function tokenize(code) {
const statements = code.split(' ');
const tokens = [];
REGEXES.forEach((REGEX) => {
statements.forEach((statement) => {
tokens.push( /* ... */ );
});
});
return tokens;
}
function lexer(tokens) {
const ast = [];
tokens.forEach((token) => ast.push( /* */ ));
return ast;
}
function parseBetterJSAlternative(code) {
const tokens = tokenize(code);
const ast = lexer(tokens);
ast.forEach((node) => // parse...);
}
优先考虑函数式编程而不是命令式编程
函数式编程更容易推理、测试和调试。虽然初学者可能难以适应这种范式,但它在代码可读性和并行编程方面有很大优势。
// 命令式编程
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].price;
}
// 函数式编程
const total = items
.map(({ price }) => price)
.reduce((total, price) => total + price);
函数链式调用
链式调用可以提高代码的可读性和灵活性。通过这种方式,可以将多个简单函数组合在一起,形成复杂的操作。
// 传统方法
class Car {
constructor({ make, model, color } = car) {}
setMake(make) {
this.make = make;
}
setModel(model) {
this.model = model;
}
setColor(color) {
this.color = color;
}
save() {
console.log(this.make, this.model, this.color);
}
}
const car = new Car('WV','Jetta','gray');
car.setColor('red');
car.save();
// 链式调用
class Car {
constructor({ make, model, color } = car){}
setMake(make) {
this.make = make;
return this;
}
setModel(model) {
this.model = model;
return this;
}
setColor(color) {
this.color = color;
return this;
}
save() {
console.log(this.make, this.model, this.color);
return this;
}
}
const car = new Car('WV','Jetta','gray')
.setColor('red')
.save();
结论
在这篇文章中,我们探讨了如何在各种编程语言中应用整洁代码的实践。整洁的函数设计可以显著提高代码的可维护性和可读性。尽管这些建议不是万能的,但它们提供了一套有效的技术,可以帮助我们应对广泛的编程挑战。总结如下:
- 使用默认参数而不是短路操作或条件赋值
- 减少函数参数(最好不多于 2 个)
- 避免副作用 - 全局变量
- 避免副作用 - 可变对象
- 函数应该只做一件事
- 函数应只有一个抽象级别
- 优先考虑函数式编程而不是命令式编程
- 函数链式调用
这些实践有助于我们编写更加清晰、简洁和高效的代码。 翻 译 —— SQ有空就喝水