详解写好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 动态添加事件代码
Nov 30 Javascript
javascript 带有滚动条的表格,标题固定,带排序功能.
Nov 13 Javascript
jQuery学习7 操作JavaScript对象和集合的函数
Feb 07 Javascript
两个比较有用的Javascript工具函数代码
Feb 17 Javascript
JavaScript 面向对象之命名空间
May 04 Javascript
jQuery EasyUI API 中文文档 - Documentation 文档
Sep 29 Javascript
解析js原生方法创建表格效率测试
Jul 08 Javascript
jquery 利用show和hidden实现级联菜单示例代码
Aug 09 Javascript
JavaScript调用ajax获取文本文件内容实现代码
Mar 28 Javascript
快速入门Vue
Dec 19 Javascript
vue 表单输入格式化中文输入法异常问题
May 30 Javascript
node中IO以及定时器优先级详解
May 10 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内存不够用的快速解决方法
2013/10/26 PHP
浅析PHP微信支付通知的处理方式
2014/05/25 PHP
ThinkPHP模板比较标签用法详解
2014/06/30 PHP
php输出xml必须header的解决方法
2014/10/17 PHP
PHP使用MPDF类生成PDF的方法
2015/12/08 PHP
PHP随机生成中文段落示例【测试网站内容时使用】
2020/04/26 PHP
laravel admin实现分类树/模型树的示例代码
2020/06/10 PHP
Ext javascript建立超链接,进行事件处理的实现方法
2009/03/22 Javascript
基于jquery的滚动新闻列表
2010/06/19 Javascript
js冒泡、捕获事件及阻止冒泡方法详细总结
2014/05/08 Javascript
For循环中分号隔开的3部分的执行顺序探讨
2014/05/27 Javascript
js超时调用setTimeout和间歇调用setInterval实例分析
2015/01/28 Javascript
angularjs2中父子组件的数据传递的实例代码
2017/07/05 Javascript
node.js操作MongoDB的实例详解
2017/10/11 Javascript
vue中的provide/inject的学习使用
2018/05/09 Javascript
pm2启动ssr失败的解决方法
2019/06/29 Javascript
JS如何调用WebAssembly编译出来的.wasm文件
2020/11/05 Javascript
[00:23]DOTA2群星共贺开放测试 25日无码时代来袭
2013/09/23 DOTA
以windows service方式运行Python程序的方法
2015/06/03 Python
Python3 读、写Excel文件的操作方法
2018/10/20 Python
python文件选择对话框的操作方法
2019/06/27 Python
Python一键安装全部依赖包的方法
2019/08/12 Python
python画蝴蝶曲线图的实例
2019/11/21 Python
解决在keras中使用model.save()函数保存模型失败的问题
2020/05/21 Python
python的help函数如何使用
2020/06/11 Python
利用pipenv和pyenv管理多个相互独立的Python虚拟开发环境
2020/11/01 Python
美国经典刺绣和字母儿童服装特卖:Smocked Auctions
2018/07/16 全球购物
意大利火车票和铁路通行证专家:ItaliaRail
2019/01/22 全球购物
北美女性服装零售连锁店:maurices
2019/06/12 全球购物
.net面试题
2016/09/17 面试题
技校毕业生的自我评价
2013/12/27 职场文书
民主评议党员自我鉴定
2014/10/21 职场文书
维稳工作情况汇报
2014/10/27 职场文书
幼儿园教学反思范文
2016/03/02 职场文书
导游词之四川武侯祠
2019/10/21 职场文书
python微信智能AI机器人实现多种支付方式
2022/04/12 Python