在使用 JavaScript 时,我们会处理很多条件,这里有 5 个提示,可让您编写更好/更清晰的条件。
1. 对多个条件使用 Array.includes
让我们看一下下面的例子:
// condition
function test(fruit) {
if (fruit == 'apple' || fruit == 'strawberry') {
console.log('red');
}
}
乍一看,上面的例子看起来不错。但是,如果我们得到更多的红色水果,比如说cherry
和cranberries
呢?我们会用更多来扩展声明||
吗?
我们可以使用(Array.includes)重写上面的条件Array.includes
function test(fruit) {
// extract conditions to array
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
if (redFruits.includes(fruit)) {
console.log('red');
}
}
我们将red fruits
(条件)提取 到一个数组中。通过这样做,代码看起来更整洁。
2. 少嵌套,早返回
让我们扩展前面的示例以包含另外两个条件:
- 如果没有提供水果,则抛出错误
- 如果超过 10 个,则接受并打印水果数量。
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// condition 1: fruit must has value
if (fruit) {
// condition 2: must be red
if (redFruits.includes(fruit)) {
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity');
}
}
} else {
throw new Error('No fruit!');
}
}
// test results
test(null); // error: No fruits
test('apple'); // print: red
test('apple', 20); // print: red, big quantity
看上面的代码,我们有:
- 1 个过滤掉无效条件的 if/else 语句
- 3 层嵌套 if 语句(条件 1、2 和 3)
我个人遵循的一般规则是在发现无效条件时尽早返回。
/_ return early when invalid conditions found _/
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// condition 1: throw error early
if (!fruit) throw new Error('No fruit!');
// condition 2: must be red
if (redFruits.includes(fruit)) {
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity');
}
}
}
通过这样做,我们减少了一层嵌套语句。这种编码风格很好,尤其是当你有很长的 if 语句时(想象一下你需要滚动到最底部才能知道有一个 else 语句,不酷)。
我们可以通过反转条件并提前返回来进一步减少嵌套。看看下面的条件 2,看看我们是如何做到的:
/_ return early when invalid conditions found _/
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
if (!fruit) throw new Error('No fruit!'); // condition 1: throw error early
if (!redFruits.includes(fruit)) return; // condition 2: stop when fruit is not red
console.log('red');
// condition 3: must be big quantity
if (quantity > 10) {
console.log('big quantity');
}
}
通过反转条件 2 的条件,我们的代码现在没有嵌套语句。当我们有很长的逻辑要处理并且我们想在条件不满足时停止进一步的处理时,这种技术很有用。
但是,这并不是执行此操作的硬性规则。问问自己,这个版本(没有嵌套)是否比前一个版本(条件 2 嵌套)更好/更具可读性?
对我来说,我会把它保留为以前的版本(条件 2 嵌套)。这是因为:
- 代码简短而直接,嵌套 if 更清晰
- 倒转状态可能会导致更多的思考过程(增加认知负荷)
因此,始终以减少嵌套和尽早返回为目标,但不要过度。如果您感兴趣,有一篇文章和 StackOverflow 讨论会进一步讨论这个主题:
- 避免否则,提早归来作者:Tim Oxley
- StackOverflow关于 if/else 编码风格的讨论
3. 使用默认函数参数和解构
我想下面的代码你可能看起来很熟悉,我们在使用 JavaScript 时总是需要检查null
/undefined
值并分配默认值:
function test(fruit, quantity) {
if (!fruit) return;
const q = quantity || 1; // if quantity not provided, default to one
console.log(`We have ${q} ${fruit}!`);
}
//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
实际上,我们可以q
通过分配默认函数参数来消除变量。
function test(fruit, quantity = 1) { // if quantity not provided, default to one
if (!fruit) return;
console.log(`We have ${quantity} ${fruit}!`);
}
//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
更容易和直观,不是吗?请注意,每个参数都可以有自己的默认函数参数。例如,我们也可以将默认值分配给fruit
:function test(fruit = 'unknown', quantity = 1)
。
如果我们fruit
是一个对象呢?我们可以分配默认参数吗?
function test(fruit) {
// printing fruit name if value provided
if (fruit && fruit.name) {
console.log (fruit.name);
} else {
console.log('unknown');
}
}
//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
看上面的例子,我们想打印水果名称,如果它可用,或者我们将打印未知。我们可以避免fruit && fruit.name
使用默认函数参数和析构进行条件检查。
// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
console.log (name || 'unknown');
}
//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
由于我们只需要name
水果的属性,我们可以使用 来解构参数{name}
,然后我们可以name
在我们的代码中使用as 变量而不是fruit.name
。
我们还将空对象指定 {}
为默认值。如果我们不这样做,您将在执行该行时出错test(undefined)
–Cannot destructure property name of 'undefined' or 'null'.
因为name
undefined 中没有属性。
如果您不介意使用 3rd 方库,有几种方法可以减少空检查:
下面是一个使用 Lodash 的例子:
// Include lodash library, you will get _
function test(fruit) {
console.log(__.get(fruit, 'name', 'unknown'); // get property name, if not available, assign default value 'unknown'
}
//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
您可以在此处运行演示代码。此外,如果您是函数式编程 (FP) 的粉丝,您可以选择使用Lodash fp,Lodash的函数版本(方法更改为get
或getOr
)。
4. 比 Switch 语句更喜欢 Map / Object Literal
让我们看看下面的例子,我们想根据颜色打印水果:
function test(color) {
// use switch case to find fruits in color
switch (color) {
case 'red':
return ['apple', 'strawberry'];
case 'yellow':
return ['banana', 'pineapple'];
case 'purple':
return ['grape', 'plum'];
default:
return [];
}
}
//test results
test(null); // []
test('yellow'); // ['banana', 'pineapple']
上面的代码看起来没什么错,但我觉得它很冗长。使用更简洁语法的对象字面量可以实现相同的结果:
// use object literal to find fruits in color
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple'],
purple: ['grape', 'plum']
};
function test(color) {
return fruitColor[color] || [];
}
或者,您可以使用Map来实现相同的结果:
// use Map to find fruits in color
const fruitColor = new Map()
.set('red', ['apple', 'strawberry'])
.set('yellow', ['banana', 'pineapple'])
.set('purple', ['grape', 'plum']);
function test(color) {
return fruitColor.get(color) || [];
}
Map是 ES2015 以来可用的对象类型,允许您存储键值对。
我们应该禁止使用 switch 语句吗?不要局限于此。就个人而言,我会尽可能使用对象字面量,但我不会设置硬性规则来阻止它,请使用对您的场景有意义的任何一种。
Todd Motto 有一篇文章深入探讨了 switch 语句与对象字面量,你可以在这里阅读。
TL; 博士; 重构语法
对于上面的示例,我们实际上可以重构我们的代码以使用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 test(color) {
// use Array filter to find fruits in color
return fruits.filter(f => f.color == color);
}
总是有不止一种方法可以达到相同的结果。我们已经用相同的例子展示了 4。编码很有趣!
5. 对所有/部分标准使用 Array.every 和 Array.some
最后一个技巧更多地是关于利用新的(但不是那么新的)Javascript Array 函数来减少代码行数。看下面的代码,我们要检查是否所有的水果都是红色的:
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
}
概括
让我们一起生成更具可读性的代码。我希望你能在这篇文章中学到一些新东西。
就这样。快乐编码!