更好的 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
假设我们想检查汽车模型是否renault
在peugeot
我们的函数中。代码可能如下所示:
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.every
或Array.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.every
或Array.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');
}
}
这看起来漂亮多了,也简洁多了,而且对我来说,逻辑也更合理。如果你还在疑惑为什么要用??
而不是||
,想想哪些值可以求值为true
或false
,你可能会得到意想不到的输出。
还有一件题外事,非常巧妙。可选链式调用也支持 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