从混乱到优雅:JavaScript 代码美化实战

工具相关 ·

每个开发者都遇到过"意大利面条"式的 JS 代码:混乱的缩进、随意的命名、混杂的逻辑。本文将通过实际案例,展示如何将混乱的代码变得优雅。

案例一:压缩代码还原

原始代码

var a=function(b,c){var d=[];for(var e=0;e<b.length;e++){if(b[e].type===c){d.push(b[e])}}return d};

第一步:格式化

var a = function(b, c) {
    var d = [];
    for (var e = 0; e < b.length; e++) {
        if (b[e].type === c) {
            d.push(b[e]);
        }
    }
    return d;
};

第二步:重命名变量

var filterByType = function(items, type) {
    var result = [];
    for (var i = 0; i < items.length; i++) {
        if (items[i].type === type) {
            result.push(items[i]);
        }
    }
    return result;
};

第三步:现代化重构

const filterByType = (items, type) => {
    return items.filter(item => item.type === type);
};

案例二:回调地狱优化

原始代码

function loadUser(id, callback) {
    getUser(id, function(user) {
        getPosts(user.id, function(posts) {
            getComments(posts[0].id, function(comments) {
                callback({
                    user: user,
                    posts: posts,
                    comments: comments
                });
            });
        });
    });
}

第一步:格式化并理解逻辑

function loadUser(id, callback) {
    getUser(id, function(user) {
        getPosts(user.id, function(posts) {
            getComments(posts[0].id, function(comments) {
                callback({
                    user: user,
                    posts: posts,
                    comments: comments
                });
            });
        });
    });
}

第二步:Promise 化

function loadUser(id) {
    return getUser(id)
        .then(user => {
            return getPosts(user.id)
                .then(posts => {
                    return getComments(posts[0].id)
                        .then(comments => {
                            return { user, posts, comments };
                        });
                });
        });
}

第三步:async/await 重构

async function loadUser(id) {
    const user = await getUser(id);
    const posts = await getPosts(user.id);
    const comments = await getComments(posts[0].id);
    return { user, posts, comments };
}

案例三:混乱的条件逻辑

原始代码

function processOrder(o){if(o){if(o.status==1){if(o.items){if(o.items.length>0){var t=0;for(var i=0;i<o.items.length;i++){t+=o.items[i].price}if(t>0){if(o.coupon){t=t*0.9}o.total=t;save(o);return true}}}}}return false}

第一步:格式化

function processOrder(o) {
    if (o) {
        if (o.status == 1) {
            if (o.items) {
                if (o.items.length > 0) {
                    var t = 0;
                    for (var i = 0; i < o.items.length; i++) {
                        t += o.items[i].price;
                    }
                    if (t > 0) {
                        if (o.coupon) {
                            t = t * 0.9;
                        }
                        o.total = t;
                        save(o);
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

第二步:提前返回,减少嵌套

function processOrder(order) {
    if (!order || order.status !== 1) return false;
    if (!order.items || order.items.length === 0) return false;
    
    let total = 0;
    for (let i = 0; i < order.items.length; i++) {
        total += order.items[i].price;
    }
    
    if (total <= 0) return false;
    
    if (order.coupon) {
        total *= 0.9;
    }
    
    order.total = total;
    save(order);
    return true;
}

第三步:函数拆分

function processOrder(order) {
    if (!isValidOrder(order)) return false;
    
    const total = calculateTotal(order.items);
    if (total <= 0) return false;
    
    order.total = applyDiscount(total, order.coupon);
    save(order);
    return true;
}

function isValidOrder(order) {
    return order 
        && order.status === 1 
        && order.items 
        && order.items.length > 0;
}

function calculateTotal(items) {
    return items.reduce((sum, item) => sum + item.price, 0);
}

function applyDiscount(total, coupon) {
    return coupon ? total * 0.9 : total;
}

案例四:重复代码消除

原始代码

// 处理用户数据
function processUserData(data) {
    var result = [];
    for (var i = 0; i < data.length; i++) {
        var item = data[i];
        if (item.active) {
            result.push({
                id: item.id,
                name: item.name.toUpperCase(),
                email: item.email.toLowerCase(),
                joined: new Date(item.joinedAt)
            });
        }
    }
    return result;
}

// 处理订单数据
function processOrderData(data) {
    var result = [];
    for (var i = 0; i < data.length; i++) {
        var item = data[i];
        if (item.active) {
            result.push({
                id: item.id,
                product: item.product.toUpperCase(),
                customer: item.customer.toLowerCase(),
                ordered: new Date(item.orderedAt)
            });
        }
    }
    return result;
}

重构:提取公共逻辑

function processActiveItems(data, transform) {
    return data
        .filter(item => item.active)
        .map(transform);
}

// 使用
const users = processActiveItems(userData, item => ({
    id: item.id,
    name: item.name.toUpperCase(),
    email: item.email.toLowerCase(),
    joined: new Date(item.joinedAt)
}));

const orders = processActiveItems(orderData, item => ({
    id: item.id,
    product: item.product.toUpperCase(),
    customer: item.customer.toLowerCase(),
    ordered: new Date(item.orderedAt)
}));

美化流程

1. 格式化

使用工具自动格式化,建立基础结构。

2. 理解逻辑

阅读代码,理解每部分的功能。

3. 重命名

将无意义的变量名改为有意义的名称。

4. 简化结构

  • 减少嵌套层级
  • 使用提前返回
  • 拆分大函数

5. 消除重复

提取公共逻辑,避免代码重复。

6. 现代化

使用 ES6+ 语法重构:

  • 箭头函数
  • 解构赋值
  • 模板字符串
  • async/await

7. 添加注释

为复杂逻辑添加注释,说明"为什么"。

工具辅助

自动化重构工具

# 使用 jscodeshift 进行代码转换
npx jscodeshift -t transform.js src/

# 使用 lebab 将 ES5 转为 ES6
npx lebab src/ -o src-es6/ --transform arrow,let,template

代码质量工具

# ESLint 自动修复
npx eslint --fix src/

# Prettier 格式化
npx prettier --write src/

最佳实践

  1. 小步迭代:不要一次性重构整个文件
  2. 测试覆盖:确保重构不破坏功能
  3. 版本控制:每次重构单独提交
  4. 代码审查:让同事审查重构代码
  5. 文档更新:更新相关文档和注释

总结

从混乱到优雅不是一蹴而就的。通过格式化、理解、重命名、简化、消除重复、现代化等步骤,可以逐步将混乱的代码变得优雅。记住:代码是写给人看的,只是顺便让机器执行。

阅读 14