本文深入解析 JavaScript 中编写清晰、可维护条件语句的六种核心技巧。通过 Array.includes 简化多条件判断,利用提前返回(Early Return)降低嵌套层级,以对象字面量或 Map 替代冗长的 Switch 语句,并结合默认参数与解构简化逻辑。此外,介绍使用 Array.every/some 进行集合验证,以及利用可选链和空值合并处理深层对象属性,帮助开发者写出更简洁、易读的代码。
前言
在任何编程语言中,代码都需要根据特定条件对输入数据做出判断,并执行相应的逻辑操作。
例如,在游戏开发中,若玩家生命值归零,则触发游戏结束逻辑;在天气应用中,若当前时间为早晨,则展示日出图片,若为夜晚,则展示星空与月亮。本文将探讨 JavaScript 中条件语句的工作原理及优化方法。
在 JavaScript 开发中,包含条件判断的代码无处不在。虽然基础的 if/else 结构看似简单,但编写高质量、高可读性的条件代码仍有诸多技巧值得掌握。以下介绍几种提升代码清晰度的实用建议。
1. 使用 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
上述代码在仅检查两种动物时看似可行,但面对不确定的用户输入时,若通过不断添加“或”语句来扩展检查项,代码将变得难以维护且逻辑混乱。
解决方案:
我们可以利用 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
在此方案中,我们将动物列表抽象为一个数组,使条件判断与核心业务逻辑分离。若需检查其他动物,只需在数组中添加新项即可。
此外,该动物数组变量可在函数作用域外定义,供代码其他部分复用。这种方法显著提升了代码的清晰度、可读性及可维护性。
2. 采用提前退出(Early Return)策略
这是一种精简代码的高效技巧。回顾我初入职场时,便学习了利用提前退出机制来优化条件逻辑。
让我们在前例基础上增加更多条件,将简单的字符串动物替换为包含特定属性的对象。
当前需求如下:
- 若未提供动物信息,抛出异常
- 打印动物类型
- 打印动物名称
- 打印动物性别
const printAnimalDetails = animal => {
let result; // declare a variable to store the final value
// condition 1: check if animal has a value
if (animal) {
// condition 2: check if animal has a type property
if (animal.type) {
// condition 3: check if animal has a name property
if (animal.name) {
// condition 4: check if animal has a gender property
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'
评估上述代码:
虽然功能正常,但代码冗长且维护困难。若不使用 Lint 工具,定位闭合花括号的位置将耗费大量时间。想象一下,若逻辑更复杂,嵌套的 if...else 语句将使代码更加晦涩难懂。
尽管可以使用三元运算符或 && 条件语法进行重构,但通过多个返回语句编写代码能显著提升清晰度。
const printAnimalDetails = ({type, name, gender } = {}) => {
if(!type) return 'No animal type';
if(!name) return 'No animal name';
if(!gender) return 'No animal gender';
// Now in this line of code, we're sure that we have an animal with all //the three properties here.
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'
在重构后的版本中,结合了对象解构与默认参数。默认参数确保即使传入 undefined 作为方法参数,我们仍拥有可解构的值(此处为空对象 {})。
在专业开发中,代码通常在这两种风格之间取得平衡。
另一个示例:
function printVegetablesWithQuantity(vegetable, quantity) {
const vegetables = ['potato', 'cabbage', 'cauliflower', 'asparagus'];
// condition 1: vegetable should be present
if (vegetable) {
// condition 2: must be one of the item from the list
if (vegetables.includes(vegetable)) {
console.log(I like ${vegetable});
// condition 3: must be large quantity
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'];
// condition 1: throw error early
if (!vegetable) throw new Error('No vegetable from the list!');
// condition 2: must be in the list
if (vegetables.includes(vegetable)) {
console.log(I like ${vegetable});
// condition 3: must be a large quantity
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!');
// condition 1: throw error early
if (!vegetables.includes(vegetable)) return;
// condition 2: return from the function is the vegetable is not in
// the list
console.log(I like ${vegetable});
// condition 3: must be a large quantity
if (quantity >= 10) {
console.log('I have bought a large quantity');
}
}
通过反转条件 2,代码消除了嵌套语句。当存在大量条件且特定条件不匹配时需停止进一步处理时,此技术尤为有用。
因此,应始终关注减少嵌套和提前返回,但需注意避免过度使用。
3. 使用对象字面量或 Map 替代 Switch 语句
让我们分析以下示例,目标是根据颜色打印对应的水果名称:
function printFruits(color) {
// use switch case to find fruits by color
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']
上述代码虽无错误,但显得冗长。相同功能可通过对象字面量以更清晰的语法实现:
// use object literal to find fruits by color
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple'],
purple: ['grape', 'plum']
};
function printFruits(color) {
return fruitColor[color] || [];
}
此外,你也可以使用 Map 实现相同功能:
// use Map to find fruits by color
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);
}
4. 默认参数与解构的应用
在 JavaScript 开发中,我们常需检查 null/undefined 值并赋予默认值,否则可能导致编译失败。
function printVegetablesWithQuantity(vegetable, quantity = 1) {
// if quantity has no value, assign 1
if (!vegetable) return;
console.log(We have ${quantity} ${vegetable}!);
}
//results
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 存在,我们希望打印 vegetable.name,否则打印“unknown”。
我们可以通过使用默认参数和解构来避免编写 if (vegetable && vegetable.name) {} 这样的条件语句。
// destructing - get name property only
// assign default empty object {}
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),将引发错误:无法从 undefined 或 null 解构属性 name,因为在 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;
// condition: all fruits must be red
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() {
// condition: short way, all fruits must be red
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() {
// condition: if any fruit is red
const isAnyRed = fruits.some(f => f.color == 'red');
console.log(isAnyRed); // true
}
6. 使用可选链和空值合并
这是两项旨在编写更清晰条件语句的 JavaScript 增强功能。撰写本文时,它们尚未被完全支持,需使用 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) // 'Fiesta'
console.log(street) // 'Some Street Name'
console.log(phoneNumber) // undefined
因此,若我们要打印车辆生产商是否来自美国,代码将如下所示:
const isManufacturerFromUSA = () => {
if(car && car.manufacturer && car.manufacturer.address &&
car.manufacturer.address.state === 'USA') {
console.log('true');
}
}
checkCarManufacturerState() // 'true'
你可以清晰地看到,当处理更复杂的对象结构时,代码会变得多么混乱。虽然有一些第三方库(如 lodash 或 idx)提供了类似功能(例如 lodash 的 _.get 方法),但 JavaScript 语言本身引入此特性无疑是一项酷功能。
这展示了这些新特性如何工作:
// 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 isManufacturerFromUSA = () => {
if(car?.manufacturer?.address?.state === 'USA') {
console.log('true');
}
}
这种写法美观且易于维护。该特性已处于 TC39 Stage 3 阶段,让我们期待其获得批准,届时我们将能广泛使用这一令人惊叹的语法。
总结
让我们学习并尝试新的技巧和技术,以编写更清晰、易维护的代码,因为几个月后,冗长的条件判断可能看起来像是搬起石头砸自己的脚。
到此这篇关于如何利用JavaScript编写更好的条件语句的文章就介绍到这了,更多相关JavaScript条件语句内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!