详解写好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 相关文章推荐
javaScript checkbox 全选/反选及批量删除
Apr 28 Javascript
QUnit jQuery的TDD框架
Nov 04 Javascript
js实现动态添加、删除行、onkeyup表格求和示例
Aug 18 Javascript
JavaScript实现的双向跨域插件分享
Jan 31 Javascript
Javascript中的包装类型介绍
Apr 02 Javascript
jquery中键盘事件小结
Feb 24 Javascript
微信小程序 地图定位简单实例
Oct 14 Javascript
javascript实现消灭星星小游戏简单版
Nov 15 Javascript
jquery实现拖动效果(代码分享)
Jan 25 Javascript
基于node.js express mvc轻量级框架实践
Sep 14 Javascript
vue组件间的参数传递实例详解
Apr 26 Javascript
vue动态合并单元格并添加小计合计功能示例
Nov 26 Vue.js
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
收音机频率指针指示不准确和灵敏度低问题
2021/03/02 无线电
PHP 学习路线与时间表
2010/02/21 PHP
phpQuery占用内存过多的处理方法
2013/11/13 PHP
php限制文件下载速度的代码
2015/10/20 PHP
注意!PHP 7中不要做的10件事
2016/09/18 PHP
php自定义函数转换html标签示例
2016/09/29 PHP
thinkphp 验证码 的使用小结
2017/05/07 PHP
构造函数+原型模式构造js自定义对象(最通用)
2014/05/12 Javascript
Jquery动态替换div内容及动态展示的方法
2015/01/23 Javascript
js鼠标点击按钮切换图片-图片自动切换-点击左右按钮切换特效代码
2015/09/02 Javascript
jQuery实现鼠标经过像翻页和描点链接效果
2016/08/08 Javascript
ionic2中使用自动生成器的方法
2018/03/04 Javascript
详解nodejs解压版安装和配置(带有搭建前端项目脚手架)
2018/12/06 NodeJs
微信小程序自定义弹窗实现详解(可通用)
2019/07/04 Javascript
详解Vue.js 作用域、slot用法(单个slot、具名slot)
2019/10/15 Javascript
[13:39]2014 DOTA2华西杯精英邀请赛 5 25 NewBee VS DK第一场
2014/05/26 DOTA
简单的Python2.7编程初学经验总结
2015/04/01 Python
Python数据结构之翻转链表
2017/02/25 Python
详解Python中 sys.argv[]的用法简明解释
2017/12/20 Python
python调用系统ffmpeg实现视频截图、http发送
2018/03/06 Python
浅析Python pandas模块输出每行中间省略号问题
2018/07/03 Python
python2.7的flask框架之引用js&css等静态文件的实现方法
2019/08/22 Python
Python中Subprocess的不同函数解析
2019/12/10 Python
Django实现celery定时任务过程解析
2020/04/21 Python
Mysql数据库反向生成Django里面的models指令方式
2020/05/18 Python
python按顺序重命名文件并分类转移到各个文件夹中的实现代码
2020/07/21 Python
Python多线程的退出控制实现
2020/08/10 Python
什么是CSS3 HSLA色彩模式?HSLA模拟渐变色条
2016/04/26 HTML / CSS
猫途鹰:全球领先的旅游点评社区
2017/04/07 全球购物
澳大利亚在线家具、灯饰和家居装饰店:LivingStyles
2018/11/20 全球购物
波兰多品牌运动商店:StreetStyle24.pl
2020/09/22 全球购物
得到Class的三个过程是什么
2012/08/10 面试题
初中高效课堂实施方案
2014/02/26 职场文书
《白鹅》教学反思
2014/04/13 职场文书
mysql外连接与内连接查询的不同之处
2021/06/03 MySQL
Apache Kafka 分区重分配的实现原理解析
2022/07/15 Servers