分享 JavaScript 条件语句的 6 个技巧

F2E 前端

什么是条件语句?

在任何编程语言中,代码都依赖输入的给定条件来做决策并执行动作的。

例如,在游戏中,如果玩家的生命值为 0,那么游戏就会结束。在天气 APP 中,如果是早上查看,就展示的日出的图片;晚上则展示星星和月亮。在这篇文章中,我们会探讨在 JavaScript 中这些所谓的条件语句是如何工作的。

如果你使用JavaScript,你将会写下大量拥有巨多涉及条件的代码。条件或许在你一开始学习时很简单,但是这不仅仅是写几个 if/else 语句。这有一些有用的提示,来帮你写出更好、更整洁的条件代码。

一、 Array.includes

针对多个条件使用 Array.includes

例如:

   function printAnimals(animal) {
   if (animal === 'dog' || animal === 'cat') {
      console.log(`I have a ${animal}`);
    }
   }

   console.log(printAnimals('dog')); // I have a dog

当我们只有两个动物要检查时,上面的代码看上去还不错。然而,我们不确定用户的输入。如果,我们还有其他动物呢?如果我们继续使用 OR 来扩展这个语句,代码将变得难以维护,并且一点儿都整洁了。

解决方案:

我们可以使用 Array.includes 来重写上面的条件

  function printAnimals(animal) {
   const animals = ['dog', 'cat', 'hamster', 'turtle']; 

   if (animals.includes(animal)) {
     console.log(`I have a ${animal}`);
   }
  }

  console.log(printAnimals('hamster')); // I have a hamster

到这儿,我们已经创建了一个动物数组,以便条件可以从代码的其余部分分别提取。现在,如果我们需要检查任意其他动物,就只需要添加新的数组项。

我们还使用在函数的范围外的变量 animals ,并在代码的任意位置重用它。这个方式写出的代码更简洁、更好懂、更易于维护,不是吗?

2. 提前退出 / 提前返回

这是一个非常酷的技巧,可以凝练你的代码并使它看起来更简洁。我记得当我开始专业地工作时,第一天就学会了编写提前退出的条件语句。

让我们以前面的示例为例,并添加多一些条件。如果 animal 不是一个简单的字符串,而是一个具有特定属性的对象该怎么办?

所以现在的要求是:

  • 如果没有 animal,请抛出错误
  • 打印 animal 的 type 值
  • 打印 animal 的 name 值
  • 打印 animal 的 gender 值
const printAnimalDetails = animal => {
  let result; // 声明一个变量来存储最终值

  // 条件 1: 检查 animal 是否具有值
  if (animal) {

    // 条件 2: 检查 animal 是否具有 type 属性
    if (animal.type) {

      // 条件 3: 检查 animal 是否具有 name 属性
      if (animal.name) {

        // 条件 4: 检查 animal 是否具有 gender 属性
        if (animal.gender) {
          result = `${animal.name} is a ${animal.gender} ${animal.type};`;
        } else {
          result = "No animal gender";
        }
      } else {
        result = "No animal name";
      }
    } else {
      result = "No animal type";
    }
  } else {
    result = "No animal";
  }

  return result;
};

console.log(printAnimalDetails()); // 'No animal'

console.log(printAnimalDetails({ type: "dog", gender: "female" })); // 'No animal name'

console.log(printAnimalDetails({ type: "dog", name: "Lucy" })); // 'No animal gender'

console.log(
  printAnimalDetails({ type: "dog", name: "Lucy", gender: "female" })
); // 'Lucy is a female dog'

你觉得上面的代码如何?

代码很好地运行,但是这样的代码又长又难以维护。如果不使用标记,你可能会浪费一些时间来弄清楚括号的位置。😄 想象一下如果代码有更复杂的逻辑会发生什么。大量的 if..else 语句!

我们可以使用三元运算符、&& 条件等来重构上述函数。但我们可以使用多个 return 语句编写更精确的代码。

const printAnimalDetails = ({type, name, gender } = {}) => {
  if(!type) return 'No animal type';
  if(!name) return 'No animal name';
  if(!gender) return 'No animal gender';

// 现在,在这行代码中,我们确定这里有一个具有三个属性的 animal。

  return `${name} is a ${gender} ${type}`;
}

console.log(printAnimalDetails()); // 'No animal type'

console.log(printAnimalDetails({ type: dog })); // 'No animal name'

console.log(printAnimalDetails({ type: dog, gender: female })); // 'No animal name'

console.log(printAnimalDetails({ type: dog, name: 'Lucy', gender: 'female' })); // 'Lucy is a female dog'

在重构版本中,还包括了 destructuringdefault parameters。默认参数确保了,如果我们将 undefined 作为方法的参数传递,我们仍会有一个能被解构的值,这里是一个空对象 {}

通常,在专业领域中,代码是在这两种方法之间的某个位置编写的。

另一个例子:

  function printVegetablesWithQuantity(vegetable, quantity) {
  const vegetables = ['potato', 'cabbage', 'cauliflower', 'asparagus'];

  // 条件 1: 应该有 vegetable
   if (vegetable) {
     // 条件 2: 必须是列表中的一项
     if (vegetables.includes(vegetable)) {
       console.log(`I like ${vegetable}`);

       // 条件 3: 必须要大量
       if (quantity >= 10) {
         console.log('I have bought a large quantity');
       }
     }
   } else {
     throw new Error('No vegetable from the list!');
   }
 }

 printVegetablesWithQuantity(null); //  No vegetable from the list!
 printVegetablesWithQuantity('cabbage'); // I like cabbage
 printVegetablesWithQuantity('cabbage', 20); 
 // 'I like cabbage`
 // 'I have bought a large quantity'

现在,我们有:

  • 1 条用于过滤无效条件的 if/else 语句
  • 3 级嵌套的 if 语句(条件 1、2 和 3)

遵循的一般规则是 发现无效条件时提前返回

  function printVegetablesWithQuantity(vegetable, quantity) {

  const vegetables = ['potato', 'cabbage', 'cauliflower', 'asparagus'];

   // 条件 1: 提前抛出错误
   if (!vegetable) throw new Error('No vegetable from the list!');

   // 条件 2: 必须在列表中
   if (vegetables.includes(vegetable)) {
      console.log(`I like ${vegetable}`);

     // 条件 3: 必须要大量
      if (quantity >= 10) {
        console.log('I have bought a large quantity');
      }
   }
 }

这样,我们就可以少一层嵌套的语句。这种编码风格非常好,特别是当你有很长的 if 语句时。

我们可以通过反转条件并提前返回来进一步减少 if 的嵌套。查看下面的条件 2,了解我们是如何做到这一点:

  function printVegetablesWithQuantity(vegetable, quantity) {

  const vegetables = ['potato', 'cabbage', 'cauliflower', 'asparagus'];

   if (!vegetable) throw new Error('No vegetable from the list!'); 
   // 条件 1: 提前抛出错误

   if (!vegetables.includes(vegetable)) return; 
   // 条件 2: vegetable 不存在时,函数提前返回
  //  the list 

  console.log(`I like ${vegetable}`);

  // 条件 3: 必须要大量
  if (quantity >= 10) {
      console.log('I have bought a large quantity');
  }
 }

通过条件 2 的反转,代码不再具有嵌套语句。当我们有很多条件、不满足任何特定条件,并且想要停止进一步的过程时,这个技巧是非常有用的。

因此,请始终以 更少嵌套 为目标,并 提前返回,但不要过度使用它。

三、字面量对象或 Map 替代 Switch 语句

让我们先看看下面的例子,我们想根据颜色打印水果:

function printFruits(color) {
  // 使用 switch case 通过颜色找水果
  switch (color) {
    case 'red':
      return ['apple', 'strawberry'];
    case 'yellow':
      return ['banana', 'pineapple'];
    case 'purple':
      return ['grape', 'plum'];
    default:
      return [];
  }
}

printFruits(null); // []
printFruits('yellow'); // ['banana', 'pineapple']

上面的代码并没有错,但是它比较冗长。我们可以用 字面量对象 这更简洁的语法来实现的结果:

// 使用字面量对象通过颜色找水果
  const fruitColor = {
    red: ['apple', 'strawberry'],
    yellow: ['banana', 'pineapple'],
    purple: ['grape', 'plum']
  };

function printFruits(color) {
  return fruitColor[color] || [];
}

亦或,你可以使用 Map 构建获得相同的结果:

// 使用 Map 通过颜色找水果
  const fruitColor = new Map()
    .set('red', ['apple', 'strawberry'])
    .set('yellow', ['banana', 'pineapple'])
    .set('purple', ['grape', 'plum']);

function printFruits(color) {
  return fruitColor.get(color) || [];
}

Map 从 ES2015 起支持的对象属性,它允许你存储 键值对

上面的例子,我们也可以使用 Array.filter 来得到相同的结果。

 const fruits = [
    { name: 'apple', color: 'red' }, 
    { name: 'strawberry', color: 'red' }, 
    { name: 'banana', color: 'yellow' }, 
    { name: 'pineapple', color: 'yellow' }, 
    { name: 'grape', color: 'purple' }, 
    { name: 'plum', color: 'purple' }
];

function printFruits(color) {
  return fruits.filter(fruit => fruit.color === color);
}

四、默认参数和解构

当使用 JavaScript 时,我们经常需要检查 null/undefined 值和赋 默认值,否则编译就会中断。

  function printVegetablesWithQuantity(vegetable, quantity = 1) { 
// 如果 quantity 没有传值,赋值 1

  if (!vegetable) return;
    console.log(`We have ${quantity} ${vegetable}!`);
  }

  // 结果
  printVegetablesWithQuantity('cabbage'); // We have 1 cabbage!
  printVegetablesWithQuantity('potato', 2); // We have 2 potato!

如果 vegetable 是一个对象呢?我们能赋默认值吗?

  function printVegetableName(vegetable) { 
    if (vegetable && vegetable.name) {
     console.log (vegetable.name);
   } else {
    console.log('unknown');
   }
 }

 printVegetableName(undefined); // unknown
 printVegetableName({}); // unknown
 printVegetableName({ name: 'cabbage', quantity: 2 }); // cabbage

在上面的例子中,我们想打印 vegetable 的 name (如果可用)或打印unknown

我们可以使用默认参数和解构来避免使用条件语句 if (vegetable && vegetable.name) {}

  // 解构 - 进获取 name 属性
  // 赋默认空对象 {}

  function printVegetableName({name} = {}) {
   console.log (name || 'unknown');
 }

 printVegetableName(undefined); // unknown
 printVegetableName({ }); // unknown
 printVegetableName({ name: 'cabbage', quantity: 2 }); // cabbage

由于我们仅仅需要 name属性,可以使用 { name } 来解构参数,然后我们就可以在代码中使用 name 作为变量来代替 vegetable.name

我们也把空对象 {} 赋值为默认值,否则,在执行 printVegetableName(undefined) 这行代码时会报错 - Cannot destructure property name of undefined or null,因为 undefined中没有属性 name。

5. 使用 Array.every 和 Array.some 匹配所有或部分条件

我们可以通过使用这些数组方法减少代码行数。查看下面的代码,我们想要检查是否所有水果的颜色都是红色:

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  let isAllRed = true;

  // 条件:所有水果都是红色
  for (let f of fruits) {
    if (!isAllRed) break;
    isAllRed = (f.color == 'red');
  }

  console.log(isAllRed); // false
}

上面的代码太长了!我们可以使用 Array.every 减少行数:

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  // 条件(短方法):所有水果都是红色
  const isAllRed = fruits.every(f => f.color == 'red');

  console.log(isAllRed); // false
}

同样,如果我们想要测试是否存在红色的水果,我们可以在一行中使用 Array.some 实现。

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
];

function test() {
  // 条件:如果存在红色的水果
  const isAnyRed = fruits.some(f => f.color == 'red');

  console.log(isAnyRed); // true
}

六、使用可选链和空值合并

这两个功能对于 使用 JavaScript 编写更简洁的条件语句非常有用。我写这该文时尚未得到完全支持,因此你需要使用 Babel 来编译代码。

可选链 允许我们在不显式检查中间节点是否存在的情况下处理树状解构,并且 空值合并 与可选链合并使用可以很好的确保不存在节点的默认值。

这儿还有例子:

   const car = {
    model: 'Fiesta',
    manufacturer: {
    name: 'Ford',
    address: {
      street: 'Some Street Name',
      number: '5555',
      state: 'USA'
      }
    }
  } 

  // 获取 car model
  const model = car && car.model || 'default model';

  // 得到 manufacturer street
  const street = car && car.manufacturer && car.manufacturer.address && 
  car.manufacturer.address.street || 'default street';

  // 请求一个不存在的 property
  const phoneNumber = car && car.manufacturer && car.manufacturer.address 
  && car.manufacturer.phoneNumber;

  console.log(model) // 'Fiesta'
  console.log(street) // 'Some Street Name'
  console.log(phoneNumber) // undefined

所以,如果我们想要打印 USA 制造的 car,代码就会像这样:

 const isManufacturerFromUSA = () => {
   if(car && car.manufacturer && car.manufacturer.address && 
 car.manufacturer.address.state === 'USA') {
     console.log('true');
   }
 }

 checkCarManufacturerState() // 'true'

你可以清楚的看到在对象解构变得复杂后这是如何的混乱。有一些第三方库,如 lodashidx,他们都有自己的函数。例如 lodash 有 _.get 方法。然而,使用 JavaScript 自身的功能酷毙了。

新功能的工作方式:

 // 获取 car 模型
 const model = car?.model ?? 'default model';

 // 获取 manufacturer 街道
 const street = car?.manufacturer?.address?.street ?? 'default street';

 // 检查 car 的 manufacturer 是否为 USA
 const isManufacturerFromUSA = () => {
   if(car?.manufacturer?.address?.state === 'USA') {
     console.log('true');
   }
 }

这就看上去漂亮极了,也更易于维护。它已经在 TC39 stage 3,让我们等它成获取批准,然后我们就可以在任何地方看到这种不可思议的语法。


总结

让我们学习并尝试新的技巧和技术,来编写更整洁、可维护的代码。因为经过几个月,这些长长的条件语句就像射在自己脚上一样。😄

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://dev.to/hellomeghna/tips-to-write...

译文地址:https://learnku.com/f2e/t/38631

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!