详解写好JS条件语句的5条守则


Posted in Javascript onFebruary 28, 2019

在用 JavaScript 工作时,我们经常和条件语句打交道,这里有5条让你写出更好/干净的条件语句的建议。

目录:

1.多重判断时使用 Array.includes

2.更少的嵌套,尽早 return

3.使用默认参数和解构

4.倾向于遍历对象而不是 Switch 语句

5.对 所有/部分 判断使用 Array.every & Array.some

6.总结

1.多重判断时使用 Array.includes

让我们看一下下面这个例子:

// condition
function test(fruit) {
 if (fruit == 'apple' || fruit == 'strawberry') {
  console.log('red');
 }
}

第一眼,上面这个例子看起来没问题。如果我们有更多名字叫 cherry 和 cranberries 的红色水果呢?我们准备用更多的 || 来拓展条件语句吗?

我们可以用 Array.includes (Array.includes)重写条件语句。

function test(fruit) {
 const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

 if (redFruits.includes(fruit)) {
  console.log('red');
 }
}

我们把红色的水果(red fruits)这一判断条件提取到一个数组。这样一来,代码看起来更整洁。

2.更少的嵌套,尽早 Return

让我们拓展上一个例子让它包含两个条件。

  • 如果没有传入参数 fruit,抛出错误
  • 接受 quantity 参数,并且在 quantity 大于 10 时打印出来
function test(fruit, quantity) {
 const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

 // 条件 1: fruit 必须有值
 if (fruit) {
  // 条件 2: 必须是red的
  if (redFruits.includes(fruit)) {
   console.log('red');

   // 条件 3: quantity大于10
   if (quantity > 10) {
    console.log('big quantity');
   }
  }
 } else {
  throw new Error('No fruit!');
 }
}

// 测试结果
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。

/_ 当发现无效语句时,尽早Return _/

function test(fruit, quantity) {
 const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

 // 条件 1: 尽早抛出错误
 if (!fruit) throw new Error('No fruit!');

 // 条件 2: 必须是红色的
 if (redFruits.includes(fruit)) {
  console.log('red');

  // 条件 3: 必须是大质量的
  if (quantity > 10) {
   console.log('big quantity');
  }
 }
}

这样一来,我们少了一层嵌套语句。这种编码风格非常好,尤其是当你有很长的if语句的时候(想象你需要滚动到最底层才知道还有else语句,这并不酷)

我们可以通过 倒置判断条件 & 尽早return 进一步减少if嵌套。看下面我们是怎么处理判断 条件2 的:

/_ 当发现无效语句时,尽早Return _/

function test(fruit, quantity) {
 const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

 // 条件 1: 尽早抛出错误
 if (!fruit) throw new Error('No fruit!');
 // 条件 2: 当水果不是红色时停止继续执行
 if (!redFruits.includes(fruit)) return; 

 console.log('red');

 // 条件 3: 必须是大质量的
 if (quantity > 10) {
  console.log('big quantity');
 }
}

通过倒置判断条件2,我们的代码避免了嵌套语句。这个技巧在我们需要进行很长的逻辑判断时是非常有用的,特别是我们希望能够在条件不满足时能够停止下来进行处理。

而且这么做并不困难。问问自己,这个版本(没有嵌套)是不是比之前的(两层条件嵌套)更好,可读性更高?

但对于我,我会保留先前的版本(包含两层嵌套)。这是因为:

  • 代码比较短且直接,包含if嵌套的更清晰
  • 倒置判断条件可能加重思考的负担(增加认知载荷)

因此,应当尽力减少嵌套和尽早return,但不要过度。如果你感兴趣的话,可以看一下关于这个话题的一篇文章和 StackOverflow 上的讨论。

  • Avoid Else, Return Early by Tim Oxley
  • StackOverflow discussion on if/else coding style

3.使用默认参数和解构

我猜下面的代码你可能会熟悉,在JavaScript中我们总是需要检查 null / undefined的值和指定默认值:

function test(fruit, quantity) {
 if (!fruit) return;
 // 如果 quantity 参数没有传入,设置默认值为 1
 const q = quantity || 1; 

 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) {
 // 如果 quantity 参数没有传入,设置默认值为 1
 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是一个object会怎么样?我们能分配一个默认参数吗?

function test(fruit) { 
 // 当值存在时打印 fruit 的值
 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 对象中可能存在的 name 属性。否则我们将打印unknown。我们可以通过默认参数以及解构从而避免判断条件 fruit && fruit.name

// 解构 - 仅仅获取 name 属性
// 为其赋默认值为空对象
function test({name} = {}) {
 console.log (name || 'unknown');
}

// test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

由于我们只需要 name 属性,我们可以用 {name} 解构出参数,然后我们就能使用变量 name 代替 fruit.name。

我们也需要声明空对象 {} 作为默认值。如果我们不这么做,当执行 test(undefined) 时,你将得到一个无法对 undefined 或 null 解构的的错误。因为在 undefined 中没有 name 属性。

如果你不介意使用第三方库,这有一些方式减少null的检查:

  • 使用 Lodash get函数
  • 使用Facebook开源的idx库(with Babeljs)

这是一个使用Lodash的例子:

function test(fruit) {
 // 获取属性名,如果属性名不可用,赋默认值为 unknown
 console.log(__.get(fruit, 'name', 'unknown'); 
}

// test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

你可以在jsbin运行demo代码。除此之外,如果你是函数式编程的粉丝,你可能选择使用 Lodash fp,Lodash的函数式版本(方法变更为get或者getOr)。

4.倾向于对象遍历而不是Switch语句

让我们看下面这个例子,我们想根据 color 打印出水果:

function test(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']

上面的代码看起来没有错误,但是我找到了一些累赘。用对象遍历实现相同的结果,语法看起来更简洁:

const fruitColor = {
 red: ['apple', 'strawberry'],
 yellow: ['banana', 'pineapple'],
 purple: ['grape', 'plum']
};

function test(color) {
 return fruitColor[color] || [];
}

或者你也可以使用 Map实现相同的结果:

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 规范之后实现的对象类型,允许你存储 key 和 value 的值。

但我们是否应当禁止switch语句的使用呢?答案是不要限制你自己。从个人来说,我会尽可能的使用对象遍历,但我并不严格遵守它,而是使用对当前的场景更有意义的方式。

Todd Motto有一篇关于 switch 语句对比对象遍历的更深入的文章,你可以在这个地方阅读

TL;DR; 重构语法

在上面的例子,我们能够用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) {
 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;

 // 条件:所有水果都是红色
 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() {
 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() {
 // 条件:任何一个水果是红色
 const isAnyRed = fruits.some(f => f.color == 'red');

 console.log(isAnyRed); // true
}

6.总结

让我们一起生产更多可读性高的代码。我希望你能从这篇文章学到东西。也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
最新优化收藏到网摘代码(digg,diigo)
Feb 07 Javascript
wordpress之js库集合研究介绍
Aug 17 Javascript
MultiSelect左右选择控件的设计与实现介绍
Jun 08 Javascript
使图片旋转的3种解决方案
Nov 21 Javascript
jQuery实现行文字链接提示效果的方法
Mar 10 Javascript
Underscore源码分析
Dec 30 Javascript
html、css和jquery相结合实现简单的进度条效果实例代码
Oct 24 Javascript
浅谈es6 javascript的map数据结构
Dec 14 Javascript
vue脚手架及vue-router基本使用
Apr 09 Javascript
如何写好一个vue组件,老夫的一年经验全在这了(推荐)
May 18 Javascript
ionic2.0双击返回键退出应用
Sep 17 Javascript
详解react组件通讯方式(多种)
May 06 Javascript
JS判断两个数组或对象是否相同的方法示例
Feb 28 #Javascript
jQuery.parseJSON()函数详解
Feb 28 #jQuery
js获取form表单中name属性的值
Feb 27 #Javascript
用VueJS写一个Chrome浏览器插件的实现方法
Feb 27 #Javascript
jQuery each和js forEach用法比较
Feb 27 #jQuery
JavaScript遍历数组的三种方法map、forEach与filter实例详解
Feb 27 #Javascript
Vue CLI 2.x搭建vue(目录最全分析)
Feb 27 #Javascript
You might like
php守护进程 加linux命令nohup实现任务每秒执行一次
2011/07/04 PHP
PHP源代码数组统计count分析
2011/08/02 PHP
php5.3中连接sqlserver2000的两种方法(com与ODBC)
2012/12/29 PHP
深入理解PHP中的Session和Cookie
2013/06/21 PHP
js操纵dom生成下拉列表框的方法
2014/02/24 Javascript
node.js中的http.get方法使用说明
2014/12/14 Javascript
自定义函数实现IE7与IE8不兼容js中trim函数的问题
2015/02/03 Javascript
Jquery on方法绑定事件后执行多次的解决方法
2016/06/02 Javascript
Flask中获取小程序Request数据的两种方法
2017/05/12 Javascript
Es6 Generator函数详细解析
2018/02/24 Javascript
Vue 中的compile操作方法
2018/02/26 Javascript
vue中的ref和$refs的使用
2018/11/22 Javascript
PHP实现基于Redis的MessageQueue队列封装操作示例
2019/02/02 Javascript
[01:08:29]DOTA2-DPC中国联赛定级赛 RNG vs Aster BO3第一场 1月9日
2021/03/11 DOTA
跟老齐学Python之正规地说一句话
2014/09/28 Python
使用Python生成随机密码的示例分享
2016/02/18 Python
virtualenv实现多个版本Python共存
2017/08/21 Python
Python面向对象之继承代码详解
2018/01/29 Python
Django在pycharm下修改默认启动端口的方法
2019/07/26 Python
python pillow库的基础使用教程
2021/01/13 Python
CSS实现鼠标滑过鼠标点击代码写法
2016/12/26 HTML / CSS
CK巴西官方网站:Calvin Klein巴西
2019/07/19 全球购物
ABOUT YOU罗马尼亚:超过600个时尚品牌
2019/09/19 全球购物
sort命令的作用和用法
2012/11/04 面试题
初中生个人学习的自我评价
2013/12/04 职场文书
自动化毕业生专业自荐书范文
2014/02/04 职场文书
节能环保口号
2014/06/12 职场文书
我的未来不是梦演讲稿
2014/09/02 职场文书
创先争优演讲稿
2014/09/15 职场文书
影视后期实训报告
2014/11/05 职场文书
学生检讨书
2015/01/27 职场文书
2015年小学数学教师个人工作总结
2015/05/25 职场文书
计算机实训心得体会
2016/01/14 职场文书
七年级之家长会发言稿范文
2019/09/04 职场文书
nginx的zabbix 5.0安装部署的方法步骤
2021/07/16 Servers
Redis实现一个账号只能登录一个设备
2022/04/19 Redis