当然,它们是创建控制流的一种简单方式,但你可以写数十亿行条件 JS 代码而不使用一个 if 语句。

在许多情况下,使用不同的结构可以更清晰地表达你想做的事 — 只要我们还在为人类编写代码,这就是我们不能忽视的。

更不用说更低的冗长度和更短的代码了...

所以:让我们看看一些强大的 if 语句升级📈。

1. AND (&&) 运算符

JavaScript 独有的 && 运算符。

使用它,我可以快速地从这个:


function visitSite(user) {
  if (user.isLoggedIn) {
    console.log(`你是 ${user.name}`);
  }
  console.log('欢迎来到 Coding Beauty。');
}

变成这个:

function visitSite(user) {
  user.isLoggedIn && console.log(`你是 ${user.name}`);
  console.log('欢迎来到 Coding Beauty。');
}

我消除了嵌套并将分支逻辑压缩成了一行。

你想在有 if 但没有对应的 else 时使用这个;特别是当 if 块只有一行时。

即使有多行,你也可以将它们抽象到一个单独的函数中,然后再次应用 &&。毕竟,我们例子中的 console.log() 本身就是一个抽象。

所以这个:

function visitSite(user) {
  if (user.isLoggedIn) {
    console.log(`欢迎回来, ${user.name}!`);
    console.log(`你上次登录是在 ${user.lastLoginDate}`);
    console.log(`你的账户类型是 ${user.accountType}`);
  }
  console.log('欢迎来到 Coding Beauty。');
}

转变为这个:

function visitSite(user) {
  user.loggedIn && handleUser(user);

  console.log('欢迎来到 Coding Beauty。');
}

function handleUser(user) {
  console.log(`欢迎回来, ${user.name}!`);
  console.log(`你上次登录是在 ${user.lastLoginDate}`);
  console.log(`你的账户类型是 ${user.accountType}`);
}

2. 三元运算符

三元运算符让我们可以将 if-else 语句压缩成一行。

当所有条件情况只涉及为同一个变量赋值时,它们是 if-else 的绝佳替代品。

这里有一个例子:


let word;

if (num === 7) {
  word = '七';
} else {
  word = '未知';
}

尽管 DRY 原则不是硬性规定,但在这种情况下,如果我们使用三元运算符来避免两次写变量赋值,事情会变得更加清晰:

const word = num === 7 ? '七' : '未知';

现在我们甚至可以使用 const 来保持不可变性和纯粹性。

嵌套的三元运算符

当我们在 if-else 语句中有 3 个或更多分支,或者我们嵌套 if 时,更清晰的三元替代方案将包含内部三元运算符。

所以这个:

const getNumWord = (num) => {
    if (num === 1) {
        return '一';
    } else if (num === 2) {
        return '二';
    } else if (num === 3) {
        return '三';
    } else if (num === 4) {
        return '四';
    } else {
        return '未知';
    }
};

变成:

const getNumWord = (num) =>
    num === 1
        ? '一'
        : num === 2
        ? '二'
        : num === 3
        ? '三'
        : num === 4
        ? '四'
        : '未知';

不过有些人确实会抱怨嵌套的三元运算符,认为它们复杂且晦涩。但我认为这更多是个人偏好问题,或者可能是格式化不佳。

说到格式化,我们有像 Prettier 这样的工具已经做这项工作(而且只做这项工作)几个世纪了:

只要有足够的缩进,你应该不会有可读性问题。如果分支比上面的更复杂,你可能想把一些低层逻辑移到前面的变量或小函数中。

说到可读性,Prettier 正在改变他们的嵌套三元风格;他们想出了一种相当独特的方式。

当前的风格巧妙地结合了扁平和树状嵌套;为真值分支中的嵌套三元添加了进一步的缩进,但保持假值分支的扁平。

const 动物名称 = 宠物.会吱吱叫()
    ? '老鼠'
    : 宠物.会吠叫()
    ? 宠物.是否可怕()
        ? '狼' // 只有在逻辑为真时才会嵌套
        : '狗'
    : 宠物.会喵喵叫()
    ? '猫'
    : 宠物.会吱吱叫() // 逻辑为假时会返回平铺结构
    ? '老鼠'
    : '可能是一只兔子';

但很快 Prettier 将把上面的格式化成这样:

const 动物名称 =
    宠物.会吱吱叫() ? '老鼠'
    : 宠物.会吠叫() ? (宠物.是否可怕() ? '狼' : '狗')
    : 宠物.会喵喵叫() ? '猫'
    : 宠物.会吱吱叫() ? '老鼠'
    : '可能是一只兔子';

主要的变化是所有的 ? 现在都在其结束的同一行的末尾,而不是下一行。

3. Switch 语句

你会在 C 风格的语言如 Java、C#和 Dart 中找到这个 — 它在这些语言中看起来完全一样,只有一些语义上的差异。

如果三元运算符最适合为一个变量生成输出,那么 switch 语句最适合处理来自一个变量的输入:

function processUserAction(action) {
    switch (action) {
        case 'play': // 如果 (action === 'play')
            console.log('Playing the game...');
            startGame({ mode: 'multiplayer' });
            break;
        case 'pause': // 否则如果 (action === 'pause')
            console.log('Pausing the game...');
            pauseGame();
            break;
        case 'stop':
            console.log('Stopping the game...');
            endGame();
            goToMainMenu();
            break;
        case 'cheat':
            console.log('Activating cheat mode...');
            enableCheatMode();
            break;
        default: // 否则
            console.log('Invalid action!');
            break;
    }
}

switch 语句的独特力量来自于能够省略每个 case 结尾的 break,让执行"落透"到下一个 case:

// 生成一个 1 到 6 的随机数来模拟掷骰子
const diceRoll = Math.floor(Math.random() * 6) + 1;

console.log(`You rolled a ${diceRoll}!`);
switch (diceRoll) {
    case 1:
        console.log('Oops, you rolled a one!');
        console.log('You lose a turn.');
        break;
    case 2:
        console.log('Two heads are better than one... sometimes');
        break;
    case 4:
    case 6:
        // 如果 (diceRoll === 2 || diceRoll === 4 || diceRoll === 6)
        console.log('Nice roll!');
        console.log('You move forward two spaces.');
        break;
    // ...
    default:
        console.log('Invalid dice roll.');
}

大多数其他有 switch-case 的语言都允许这样做,值得注意的例外是 C#。

4. 键值对象

键值对象让我们可以声明式地将输入映射到输出。

const weatherActivities = {
    sunny: 'go to the beach', // 晴天:去海滩
    rainy: 'watch a movie at home', // 雨天:在家看电影
    cloudy: 'take a walk in the park', // 阴天:去公园散步
    snowy: 'build a snowman', // 雪天:堆雪人
};

const weather = 'sunny';

console.log(`Okay, so right now it's ${weather}.`); // 好的,现在是晴天。
console.log(`Why don't you ${weatherActivities[weather]}?`); // 你为什么不去海滩呢?

当我在 React Native 应用中创建屏幕时,这对我来说真的很棒 — 每个屏幕都有自己的加载、错误和成功状态。

这里是我们应用中一个屏幕的片段:

// screens/course-list.tsx
// …

import {
  ActivityIndicator,
  Text
  // …
} from 'react-native-paper';

type ViewState = 'loading' | 'error' | 'data';

export function Courses({ route, navigation }) {
  const [viewState, setViewState] = useState<ViewState>('loading');
  const state = useAppState();
  const courses = state?.courseList;

  // …

  const contentDict = {
    loading: <ActivityIndicator />,
    error: <Text>Error</Text>,
    data: <CourseList courses={courses} />,
  };

  const content = contentDict[viewState];

  return (
    <View>
      <StatusBar />
      {content}
    </View>
  );
}

// …

最后的思考

所以 if 语句一点也不坏,它们非常适合以容易理解的方式编写条件逻辑。

但重要的是要考虑替代方法,使代码更短、更清晰,甚至更具表现力。这不是要完全消除 if 语句,而是采用有效的技术,使我们的代码更高效、更优雅。

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68k 声望104.9k 粉丝