今年的目标:
写出让人想收藏的代码,而不是让人想重写的代码
先问自己几个问题:
如果答案是 有,那这篇文章就是为你准备的。
好代码的三个境界:
你是第几层呢?如何做一个优雅的程序员呢?
📝 第一章:有意义的变量命名
反面教材:
// 猜猜这些变量是干什么的?let a = 10;let b = 20;let c = a + b;let d = getUserData();
优雅版本:
// 一目了然!const BASE_PRICE = 10;const DISCOUNT = 20;const FINAL_PRICE = BASE_PRICE - DISCOUNT;const currentUser = getCurrentUserProfile();
命名黄金法则:
1. 别怕名字长,怕的是看不懂
// ❌ 太短let d = new Date();// ✅ 清晰let currentDate = new Date();let invoiceCreationDate = new Date();
2. 使用具体名词,避免泛称
// ❌ 太泛let data = fetchData();// ✅ 具体let userOrders = fetchUserOrders();let productInventory = getProductInventory();
3. 布尔值用 is/has/can/should 开头
// ❌ 不明所以let loading = true;// ✅ 清晰明了let isLoading = true;let hasPermission = false;let canEditDocument = true;
4. 函数名用动词开头
// ❌ 名词开头,像变量function userData() { ... }// ✅ 动词开头,表示动作function getUserData() { ... }function calculateTotalPrice() { ... }function validateEmailFormat() { ... }
实战练习:
// 重构前function p(d) { let r = []; for (let i = 0; i < d.length; i++) { if (d[i].a > 18) { r.push(d[i]); } } return r;}// 重构后 - 猜猜功能是什么?function filterAdults(users) { const adultUsers = []; for (let i = 0; i < users.length; i++) { if (users[i].age > 18) { adultUsers.push(users[i]); } } return adultUsers;}
💬 第二章:合理的代码注释
注释的三大误区:
误区1:注释代码在做什么(废话注释)
// ❌ 废话// 增加计数器count++;// ❌ 更废话的// 这是一个for循环,从0开始,到10结束for (let i = 0; i < 10; i++) { // 这里打印i console.log(i);}
误区2:用注释代替好代码
// ❌ 注释弥补烂代码// 检查用户是否满18岁且不是学生if (u.a > 18 && !u.s) { // ...}// ✅ 好代码自己会说话const isAdultNonStudent = user.age > 18 && !user.isStudent;if (isAdultNonStudent) { // ...}
误区3:过时的注释(比没注释更可怕)
// ❌ 代码改了,注释没改// 这里返回用户ID(2022年写的注释)function getUserId() { return user.uuid; // 2023年改成uuid了!}
应该写什么样的注释?
1. 解释「为什么」,而不是「是什么」
// ✅ 好注释:解释为什么这么做// 使用快速排序而不是数组的sort方法,因为需要稳定排序function sortUserByAge(users) { // 快速排序实现...}// ❌ 坏注释:解释显而易见的事情// 这个函数用来排序用户function sortUsers(users) { users.sort((a, b) => a.age - b.age);}
2. 复杂的业务逻辑需要注释
// ✅ 复杂的业务规则需要解释/** * 计算用户折扣 * 规则: * 1. VIP用户:8折 * 2. 新用户首单:9折 * 3. 节假日促销:额外95折 * 4. 多重优惠取最低价 */functioncalculateUserDiscount(user, order) { // 复杂的计算逻辑...}
3. 公开API需要文档注释
// ✅ 公共函数需要完整文档/** * 创建新用户账户 * @param {Object} userInfo - 用户信息 * @param {string} userInfo.username - 用户名,3-20字符 * @param {string} userInfo.email - 邮箱地址 * @param {number} userInfo.age - 年龄,必须≥18 * @returns {Promise<Object>} 创建的用户对象 * @throws {ValidationError} 参数验证失败 * @example * const user = await createUser({ * username: 'john_doe', * email: 'john@example.com', * age: 25 * }); */async function createUser(userInfo) { // 实现...}
4. 标记TODO和FIXME
// ✅ 合理的标记function processPayment(order) { // TODO: 需要添加支付失败重试逻辑 // FIXME: 这里的汇率转换有精度问题 // HACK: 临时解决跨域问题,需要后端配合 try { // 支付逻辑... } catch (error) { // XXX: 这里的错误处理需要优化 }}
注释最佳实践清单:
🎨 第三章:简洁的函数设计
函数设计的「三不原则」:
1. 不要太大(一个函数只做一件事)
// ❌ 函数做了太多事function processUserOrder(user, products, payment) { // 验证用户 if (!user.isValid) throw new Error('Invalid user'); // 验证商品 products.forEach(p => { if (!p.inStock) throw new Error('Out of stock'); }); // 计算总价 let total = products.reduce((sum, p) => sum + p.price, 0); // 处理支付 const paymentResult = processPayment(payment, total); // 发送邮件 sendOrderConfirmationEmail(user.email, products); // 更新库存 updateInventory(products); // 返回结果 return { success: true, orderId: generateId() };}// ✅ 拆分成小函数function processUserOrder(user, products, payment) { validateUser(user); validateProducts(products); const total = calculateTotal(products); const paymentResult = processPayment(payment, total); sendOrderConfirmation(user, products); updateInventory(products); return createOrderResponse();}
2. 参数不要太多(≤3个为宜)
// ❌ 参数太多function createUser(username, email, password, age, gender, city, country, phone) { // ...}// ✅ 使用对象参数function createUser(userData) { const { username, email, password, age, gender, city, country, phone } = userData; // ...}// 或者进一步优化class User { constructor(data) { this.username = data.username; this.email = data.email; // ... }}
3. 嵌套不要太深(≤3层)
// ❌ 嵌套地狱function processData(data) { if (data) { if (data.isValid) { if (data.values) { data.values.forEach(value => { if (value > 0) { // 真正的逻辑在这里... } }); } } }}// ✅ 提前返回,减少嵌套function processData(data) { if (!data) return; if (!data.isValid) return; if (!data.values) return; data.values.forEach(value => { if (value <= 0) return; // 真正的逻辑在这里... });}
函数设计的实用技巧:
技巧1:函数就像一个好故事(有开头、有过程、有结尾)
// ✅ 好故事:清晰的结构function calculateOrderPrice(order) { // 开头:准备数据 const basePrice = calculateBasePrice(order.items); const discounts = calculateDiscounts(order.coupons); // 过程:核心计算 const priceAfterDiscount = applyDiscounts(basePrice, discounts); const tax = calculateTax(priceAfterDiscount, order.taxRate); const shipping = calculateShipping(order.address); // 结尾:返回结果 return { subtotal: basePrice, discount: discounts, tax: tax, shipping: shipping, total: priceAfterDiscount + tax + shipping };}
技巧2:使用纯函数(同样的输入,永远得到同样的输出)
// ❌ 不纯的函数(有副作用)let counter = 0;function increment() { counter++; // 修改了外部状态 return counter;}// ✅ 纯函数function add(a, b) { return a + b; // 只依赖参数,不修改外部状态}// ✅ 另一个纯函数function increment(currentValue) { return currentValue + 1; // 返回新值,不修改原值}
技巧3:善用默认参数和参数解构
// ❌ 手动检查参数functioncreatePost(title, content, author) { if(!author) author = 'Anonymous'; // ...}// ✅ 使用默认参数functioncreatePost(title, content, author = 'Anonymous') { // ...}// ✅ 使用参数解构functioncreatePost({ title, content, author = 'Anonymous', tags = [] }) { // ...}// 调用更清晰createPost({ title: 'Hello World', content: 'My first post', tags: ['javascript', 'programming']});
函数长度检查清单:
如果你的函数:
实战:代码优雅度自测
给你的代码打个分(每题1-5分):
命名(总分15)
- [ ] 布尔变量用is/has/can开头吗?(1-5分)
注释(总分15)
- [ ] 注释解释「为什么」而不是「是什么」吗?(1-5分)
函数(总分15)
评分结果:
今天就能做的3件事:
- 花10分钟,找一个你最近写的函数,按照今天的方法重构它
- 定个规矩:每次提交代码前,问自己「三个月后能看懂吗?」
- 找个伙伴:互相Review代码,给对方提一个「优雅度建议」
最后记住:
优雅的代码不是写给机器看的,是写给人看的——写给三个月后的自己,写给需要维护它的同事,写给所有热爱编程的人。