JavaScript优雅写if条件语句5大技巧
@(译文)[JavaScript]
该篇是 Tips to write better Conditionals in JavaScript 的译文,原文链接。
[TOC]
什么是条件判断
在任何编程语言中,程序需要根据给定的条件做进一步的处理。 比如,在一游戏中,玩家的生命指数降为0了,那么游戏将结束。在一款天气app中,在早晨的时候,app背景是日出;在夜晚的时候,app背景则为星空。在这篇文章中,我将介绍如何写优雅的if条件判断语句。
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
当只有两种animal时,上面的代码看起来没有什么问题。但假如又有其他animal,将不得不在现有的条件中,用 || 运算符追加更多的animal,代码将慢慢变得不简洁及难以维护。
解决方法:
用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 数组,将animal从 if 语句中抽取出来了。如果有更多的animal满足条件,只需要将动物放入到animals数组中即可。 也可以将animals 数组声明为全局变量,这样就可以在更多的地方重用animals。通过Array.includes进行条件判断,代码是简洁、通俗易懂、便于维护。
2. 提前 exit / return
这是一个使你代码精炼、整洁的有效技巧。我记得当我第一天正式工作的时候,我尝试用提前 exit写条件判断。 让我们回顾一下之前的例子,去添加更多的animal,万一这些动物不是一个简单的string,而是有特定属性的对象。 现在程序要求如下:
- 没有animal,抛出错误
- 打印animal的类型
- 打印animal的名字
- 打印animal的性别就退出程序
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'
你感觉上面的代码写的如何? 它可以正常运行,但是又臭又长,如果没有语法高亮,在排查if语句是否正确闭合,就可能花费很长时间。假如这代码有更复杂的逻辑,无法想象的一大坨 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';
// 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'
我们注意到:
- 一个 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 语句的时候。 通过判断取反和提前退出程序,我们可以进一步减少 if 语句嵌套。看下面的进一步代码优化,留意 condition 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');
}
}
对 condition 2 判断取反,上面代码不再有 if 语句嵌套。当我们有大量的判断的时候,并且在某一条件不满足时,就不将进行下一步处理时,判断取反,提前退出技巧是及其有用的。
我们通过 Return Early,减少代码嵌套。但是不要在不适用提前退出的场景下,勉强使用 Return Early。
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引入的 object 类型,Map 可以存放键值对。 使用 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 ,然后赋一个默认值。如果不判断值是否为 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 是一个 object,那么如何赋一个默认参数值?
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) 将报错 - Cannot destructure property ‘name’ of ‘undefined’ as it is undefined,因为 undefined 没有name属性。
5. 用 Array.every & Array.some 进行判断
运用 array 方法可以简化代码,下面的代码片段,判断 fruits 颜色是不是都是 red:
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
}
同理,如果我们想判断 fruits 其中有颜色为 red 的 fruit,可以用 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
}
总结
越来越冗长的判断,在未来的某一天终将变成shi:hankey:山。从现在开始,让我们一起尝试使用上述5大技巧,写更简洁、更易维护的代码。
原文
Tips to write better Conditionals in JavaScript