为了根据不同的条件执行不同的逻辑,传统的做法是使用 switch 语句或者多个 if-else 分支,但这些方法往往会让代码变得冗长且难以维护。
传统 switch 语句的痛点
让我们先看一个典型的 switch 语句示例:
function getStatusMessage(status) {
switch (status) {
case 'loading':
return '正在加载...';
case 'success':
return '操作成功!';
case 'error':
return '操作失败,请重试';
case 'timeout':
return '请求超时';
default:
return '未知状态';
}
}这种写法存在几个问题:
- 代码冗长:每个 case 都需要写 break 语句
- 易出错:忘记写 break 会导致贯穿执行
- 可读性差:大量的样板代码影响核心逻辑的阅读
- 难以扩展:添加新条件需要修改函数内部
对象映射:更简洁的替代方案
基础用法
使用对象映射,上面的代码可以简化为:
const statusMessages = {
loading: '正在加载...',
success: '操作成功!',
error: '操作失败,请重试',
timeout: '请求超时'
};
function getStatusMessage(status) {
return statusMessages[status] || '未知状态';
}优势显而易见:
- 代码量减少了 60%
- 逻辑更清晰,一目了然
- 不会出现忘记 break 的问题
函数映射:处理复杂逻辑
当需要执行复杂逻辑时,可以将值设为函数:
const userActions = {
login: (user) => {
console.log(`用户 ${user.name} 登录成功`);
return { success: true, token: generateToken(user) };
},
logout: (user) => {
console.log(`用户 ${user.name} 已退出`);
clearUserSession(user.id);
return { success: true };
},
register: (userData) => {
const newUser = createUser(userData);
console.log(`新用户 ${newUser.name} 注册成功`);
return { success: true, user: newUser };
}
};
function handleUserAction(action, data) {
const handler = userActions[action];
if (handler) {
return handler(data);
}
throw new Error(`不支持的操作: ${action}`);
}
// 辅助函数(假设已实现)
function generateToken(user) {
return 'mock-token-' + user.name;
}
function clearUserSession(userId) {
console.log(`清除用户 ${userId} 的会话`);
}
function createUser(userData) {
return { id: 'user-' + Date.now(), name: userData.name };
}
// 使用示例
const result = handleUserAction('login', { name: 'Alice', password: '123456' });
console.log(result); // 输出: { success: true, token: 'mock-token-Alice' }Map 对象:更强大的键值映射
对于需要非字符串键的场景,可以使用 Map 对象:
const responseHandlers = new Map([
[200, () => console.log('请求成功')],
[404, () => console.log('资源未找到')],
[500, () => console.log('服务器错误')],
['default', () => console.log('未知错误')]
]);
function handleResponse(statusCode) {
const handler = responseHandlers.get(statusCode) || responseHandlers.get('default');
handler();
}
// 使用示例
handleResponse(200); // 输出: "请求成功"
handleResponse(404); // 输出: "资源未找到"
handleResponse(999); // 输出: "未知错误"高级应用场景
多条件映射
const discountRules = {
'vip-new': 0.8, // VIP新用户8折
'vip-old': 0.9, // VIP老用户9折
'normal-new': 0.95, // 普通新用户95折
'normal-old': 1.0 // 普通老用户原价
};
function calculatePrice(userType, isNewUser, originalPrice) {
const key = `${userType}-${isNewUser ? 'new' : 'old'}`; // 修复字符串拼接
const discount = discountRules[key] || 1.0; // 默认无折扣
return originalPrice * discount;
}
// 使用示例
console.log(calculatePrice('vip', true, 100)); // VIP新用户: 100*0.8 = 80
console.log(calculatePrice('vip', false, 100)); // VIP老用户: 100*0.9 = 90
console.log(calculatePrice('normal', true, 100)); // 普通新用户: 100*0.95 = 95
console.log(calculatePrice('normal', false, 100));// 普通老用户: 100*1.0 = 100
console.log(calculatePrice('invalid', true, 100));// 未知类型: 100*1.0 = 100 (默认)性能对比
让我们来看看性能差异:
// 测试数据
const testCases = Array.from({ length: 10000 }, () =>
['loading', 'success', 'error', 'timeout'][Math.floor(Math.random() * 4)]
);
// Switch 版本
function switchVersion(status) {
switch (status) {
case 'loading': return '正在加载...';
case 'success': return '操作成功!';
case 'error': return '操作失败,请重试';
case 'timeout': return '请求超时';
default: return '未知状态';
}
}
// 对象映射版本
const objectMapping = {
loading: '正在加载...',
success: '操作成功!',
error: '操作失败,请重试',
timeout: '请求超时'
};
function objectVersion(status) {
return objectMapping[status] || '未知状态';
}
// 性能测试
console.time('Switch版本');
testCases.forEach(switchVersion);
console.timeEnd('Switch版本');
console.time('对象映射版本');
testCases.forEach(objectVersion);
console.timeEnd('对象映射版本');通常情况下,对象映射的性能会略优于 switch 语句,特别是在分支较多的情况下。
当然,这并不意味着要完全抛弃 switch 语句。在选择使用哪种方案时,应该根据具体的业务场景和需求来决定。对于简单的映射关系,对象映射是更好的选择;对于复杂的条件判断和控制流,switch 语句可能更加合适。