T

Tips and Tricks for Better JavaScript Conditionals and Match Criteria

2025-06-07

更好的 JavaScript 条件和匹配标准的技巧和窍门

最初发布于devinduct.com

介绍

如果你像我一样喜欢简洁的代码,你会尝试在代码中尽量少用条件语句。通常,面向对象编程使我们能够避免条件语句,并用多态和继承来代替它们。我认为我们应该尽可能遵循这些原则。

正如我在另一篇文章《JavaScript 清洁代码最佳实践》中提到的那样,您编写的代码不仅仅是为了机器,您还为“未来的自己”“其他人”编写代码。

另一方面,由于各种原因,我们最终可能会在代码中使用条件语句。也许是我们修复 bug 的截止日期很紧,或者不使用条件语句会对我们的代码库造成太大的改动,等等。本文旨在帮助您解决这些情况,并帮助您整理所使用的条件语句。

尖端

以下是一些关于如何组织if...else陈述以及如何用更少的文字实现更多目标的技巧。尽情享受吧!

1. 要事优先,琐碎,但绝非琐碎

不要使用否定条件(它们可能会造成混淆),并且boolean变量名要使用条件简写。我再怎么强调也不为过,尤其是关于否定条件的部分。这是一种不自然的做法。

坏的

const isEmailNotVerified = (email) => {
  // implementation
}

if (!isEmailNotVerified(email)) {
  // do something...
}

if (isVerified === true) {
  // do something...
}

好的

const isEmailVerified = (email) => {
  // implementation
}

if (isEmailVerified(email)) {
  // do something...
}

if (isVerified) {
  // do something...
}

现在,当我们清楚了上述事项后,我们就可以开始了。

2. 对于多个条件,使用Array.includes

假设我们想检查汽车模型是否renaultpeugeot我们的函数中。代码可能如下所示:

const checkCarModel = (model) => {
  if(model === 'renault' || model === 'peugeot') { 
    console.log('model valid');
  }
}

checkCarModel('renault'); // outputs 'model valid'

考虑到我们只有两个模型,这样做似乎可以接受,但如果我们想对另一个模型进行检查呢?或者更多模型呢?如果我们添加更多or语句,代码将更难维护,而且不太干净。为了使其更简洁,我们可以将函数重写如下:

const checkCarModel = (model) => {
  if(['peugeot', 'renault'].includes(model)) { 
    console.log('model valid');
  }
}

checkCarModel('renault'); // outputs 'model valid'

上面的代码看起来已经很不错了。为了让它更美观,我们可以创建一个变量来保存汽车型号:

const checkCarModel = (model) => {
  const models = ['peugeot', 'renault'];

  if(models.includes(model)) { 
    console.log('model valid');
  }
}

checkCarModel('renault'); // outputs 'model valid'

现在,如果我们想对更多模型进行检查,只需添加一个新的数组项即可。此外,如果变量很重要,我们可以models在函数作用域之外的某个地方声明该变量,并在需要的地方重用它。这样,我们就可以将变量集中起来,并且由于我们只需要在代码中修改这一个地方,因此维护起来非常方便。

3. 要匹配所有条件,请使用Array.everyArray.find

在这个例子中,我们想要检查每个车型是否都是传递给函数的车型。为了更有效地实现这一点imperative,我们可以这样做:

const cars = [
  { model: 'renault', year: 1956 },
  { model: 'peugeot', year: 1968 },
  { model: 'ford', year: 1977 }
];

const checkEveryModel = (model) => {
  let isValid = true;

  for (let car of cars) {
    if (!isValid) {
      break;
    }
    isValid = car.model === model;
  }

  return isValid;
}

console.log(checkEveryModel('renault')); // outputs false

如果您更喜欢命令式的写法,上面的代码可能没问题。另一方面,如果您不关心底层实现,您可以重写上面的函数并使用Array.everyArray.find来实现相同的结果。

const checkEveryModel = (model) => {
  return cars.every(car => car.model === model);
}

console.log(checkEveryModel('renault')); // outputs false

通过使用Array.find,稍加调整我们就可以实现相同的结果,并且性能应该相同,因为两个函数都对数组中的每个元素执行回调,并在发现虚假false项目时立即返回。

const checkEveryModel = (model) => {
  return cars.find(car => car.model !== model) === undefined;
}

console.log(checkEveryModel('renault')); // outputs false

4. 匹配部分标准时使用Array.some

与所有条件一样Array.every,此方法可以非常轻松地检查数组是否包含一个或多个项目。为此,我们需要提供一个回调并根据条件返回一个布尔值。

for...loop我们可以通过编写类似上面的语句来实现相同的结果,但幸运的是,我们有很酷的 JavaScript 函数可以为我们完成这些事情。

const cars = [
  { model: 'renault', year: 1956 },
  { model: 'peugeot', year: 1968 },
  { model: 'ford', year: 1977 }
];

const checkForAnyModel = (model) => {
  return cars.some(car => car.model === model);
}

console.log(checkForAnyModel('renault')); // outputs true

5. 尽早返回而不是if...else分支

当我还是学生的时候,我被教导说一个函数应该只有一个返回语句,并且它应该只从一个位置返回。如果处理得当,这不失为一个好方法,这意味着我们应该意识到它何时会导致条件嵌套的困境。if...else如果失控,多重分支和嵌套可能会很麻烦。

另一方面,如果代码库很大,包含很多行代码,那么在代码深处的某个地方出现一个 return 语句就会有问题。如今我们实践关注点分离和 SOLID 原则,因此,代码行数过多的情况应该很少见。

让我们创建一个例子来说明这一点,假设我们想要显示给定汽车的型号和制造年份。

const checkModel = (car) => {
  let result; // first, we need to define a result value

  // check if car exists
  if(car) {

    // check if car model exists
    if (car.model) {

      // check if car year exists
      if(car.year) {
        result = `Car model: ${car.model}; Manufacturing year: ${car.year};`;
      } else {
        result = 'No car year';
      }

    } else {
      result = 'No car model'
    }   

  } else {
    result = 'No car';
  }

  return result; // our single return statement
}

console.log(checkModel()); // outputs 'No car'
console.log(checkModel({ year: 1988 })); // outputs 'No car model'
console.log(checkModel({ model: 'ford' })); // outputs 'No car year'
console.log(checkModel({ model: 'ford', year: 1988 })); // outputs 'Car model: ford; Manufacturing year: 1988;'

如你所见,即使对于我们这个简单的问题来说,上面的代码也相当长。想象一下,如果我们有更复杂的逻辑会发生什么。if...else语句太多了。

我们可以分更多步骤重构上面的函数,使其在各个步骤中都更加完善。例如,使用三元运算符、包含&&条件等等,但我将直接跳到最后,向您展示如何通过使用现代 JavaScript 特性和多个 return 语句来使其变得极其简化。

const checkModel = ({model, year} = {}) => {
  if(!model && !year) return 'No car';
  if(!model) return 'No car model';
  if(!year) return 'No car year';

  // here we are free to do whatever we want with the model or year
  // we made sure that they exist
  // no more checks required

  // doSomething(model);
  // doSomethingElse(year);

  return `Car model: ${model}; Manufacturing year: ${year};`;
}

console.log(checkModel()); // outputs 'No car'
console.log(checkModel({ year: 1988 })); // outputs 'No car model'
console.log(checkModel({ model: 'ford' })); // outputs 'No car year'
console.log(checkModel({ model: 'ford', year: 1988 })); // outputs 'Car model: ford; Manufacturing year: 1988;'

在重构版本中,我们引入了解构和默认参数。默认参数将确保在传递 时,我们有一个值可以析构undefined。请注意,如果传递一个null值,函数将抛出错误,而这正是之前方法的优势,因为在这种情况下,null传递 时,输出将是'No car'

对象解构将确保函数只获取它需要的内容。例如,如果我们在给定的 car 对象中添加了额外的属性,那么该属性在我们的函数内部将无法使用。

根据个人偏好,开发人员会选择其中一种方式。实践告诉我,代码通常写在这两种方法之间。许多人认为if...else语句更容易理解,这有助于他们更轻松地理解程序流程。

6. 使用索引或映射代替switch语句

假设我们想根据给定的状态获取汽车模型。

const getCarsByState = (state) => {
  switch (state) {
    case 'usa':
      return ['Ford', 'Dodge'];
    case 'france':
      return ['Renault', 'Peugeot'];
    case 'italy':
      return ['Fiat'];
    default:
      return [];
  }
}

console.log(getCarsByState()); // outputs []
console.log(getCarsByState('usa')); // outputs ['Ford', 'Dodge']
console.log(getCarsByState('italy')); // outputs ['Fiat']

上面的代码可以重构以switch完全排除该语句。

const cars = new Map()
  .set('usa', ['Ford', 'Dodge'])
  .set('france', ['Renault', 'Peugeot'])
  .set('italy', ['Fiat']);

const getCarsByState = (state) => {
  return cars.get(state) || [];
}

console.log(getCarsByState()); // outputs []
console.log(getCarsByState('usa')); //outputs ['Ford', 'Dodge']
console.log(getCarsByState('italy')); // outputs ['Fiat']

或者,我们可以为每个州创建一个包含可用车辆列表的类,并在需要时使用它,但这是另一篇文章的主题。这篇文章是关于条件语句的。更合适的做法是使用对象字面量。

const carState = {
  usa: ['Ford', 'Dodge'],
  france: ['Renault', 'Peugeot'],
  italy: ['Fiat']
};

const getCarsByState = (state) => {
  return carState[state] || [];
}

console.log(getCarsByState()); // outputs []
console.log(getCarsByState('usa')); // outputs ['Ford', 'Dodge']
console.log(getCarsByState('france')); // outputs ['Renault', 'Peugeot']

7. 使用可选链和空值合并

本节我可以用“终于”来开头。在我看来,这两个功能对 JavaScript 语言来说非常有用。作为一个来自 C# 世界的人,我可以说我经常使用它们。

在撰写本文时,这些选项尚未完全支持,您需要使用 Babel 来编译以这种方式编写的代码。您可以在此处查看可选链,并在此处查看空值合并

可选链使我们能够处理树状结构,而无需明确检查中间节点是否存在,而空值合并与可选链结合使用效果很好,可用于确保不存在的节点的默认值。

让我们用一些例子来支持上面的陈述,并从旧的做事方式开始。

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

// to get the car model
const model = car && car.model || 'default model';
// to get the manufacturer street
const street = car && car.manufacturer && car.manufacturer.address && car.manufacturer.address.street || 'default street';
// request an un-existing property
const phoneNumber = car && car.manufacturer && car.manufacturer.address && car.manufacturer.phoneNumber;

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

因此,如果我们想打印出汽车制造商是否来自美国,代码将如下所示:

const checkCarManufacturerState = () => {
  if(car && car.manufacturer && car.manufacturer.address && car.manufacturer.address.state === 'USA') {
    console.log('Is from USA');
  }
}

checkCarManufacturerState() // outputs 'Is from USA'

如果对象结构更复杂,这会变得多么混乱,这一点无需赘述。许多库,例如 lodash,都有自己的函数作为变通方案,但我们不希望这样,我们希望能够用原生 JS 来实现。让我们看看一种新的实现方式。

// to get the car model
const model = car?.model ?? 'default model';
// to get the manufacturer street
const street = car?.manufacturer?.address?.street ?? 'default street';

// to check if the car manufacturer is from the USA
const checkCarManufacturerState = () => {
  if(car?.manufacturer?.address?.state === 'USA') {
    console.log('Is from USA');
  }
}

这看起来漂亮多了,也简洁多了,而且对我来说,逻辑也更合理。如果你还在疑惑为什么要用??而不是||,想想哪些值可以求值为truefalse,你可能会得到意想不到的输出。

还有一件题外事,非常巧妙。可选链式调用也支持 DOM API,这非常酷,这意味着你可以这样做:

const value = document.querySelector('input#user-name')?.value;

结论

好的,目前为止就这些。如果你喜欢这篇文章,请在devinduct.com上订阅(每篇文章末尾都有一个小表格 :)),或者在Twitter上关注我,随时了解最新动态。

感谢您的阅读,我们下篇文章再见。

文章来源:https://dev.to/proticm/tips-and-tricks-for-better-javascript-conditionals-and-match-criteria-409f
PREV
避免混乱的 git 历史记录 避免混乱的 git 历史记录
NEXT
处理数组重复可能会比较棘手 进一步阅读