深入了解JavaScript的逻辑运算符(与、或)


Posted in Javascript onDecember 20, 2016

十二月已经过半,冬季是一个美妙的季节,寒冷的空气逼得人们不得不躲在安逸舒适的环境里生活。冬季会给人一种安静祥和的氛围,让人沉浸在其中,仿佛是一个旧的阶段的结束,同时也是一个新的阶段的开始。这么说来,西方和中国的圣诞节和春节都选择在了冬季也不是没有道理,在一年中最寒冷的时候,人们拥簇在温暖的环境里,彼此诉说着过去一年里自己的成就,展望着新的一年里美好的愿望,相互挂念的人团聚,天气的寒冷和人情的温暖形成了强烈的对比。而在天寒地冻之中,仿佛更有利于人们思考,去探寻知识的真谛。

这次想分享的是 JS 当中的逻辑运算符与、或,也就是 && 、 || ,初来乍到的同学们看到这里就会觉得没趣了,这玩意有什么好分享的,刚开始学 JS 的时候不就会了吗,我用了无数遍都没有什么问题啊。而有经验的同学可能会陷入沉思,难不成这其中会有什么奥秘所在?没错,别看这简简单单的几个运算符,虽然这是最基础的知识,但其中隐藏的奥秘却十分耐人寻味,接下来我就为大家一一揭开这简答的运算符背后的奇妙之处。

基础的作用我就不说了,这两个符号是个程序员都能明白,这里首先我想先来说一说 JS 当中的隐式转换。

众所周知,JS 在做逻辑判断的时候会自动将非布尔类型的值进行隐式转换,转换成布尔类型的值然后在进行逻辑运算。在初学 JS 的时候,都会讲到在隐式转换中,除了几个特定的假值,其他的均会转换成真值,这些假值有:

NaN;
 "";
 undefined;
 null;
 0;

有了这些隐式转换的规则,便构成了 JS 当中逻辑运算的核心基础。

其实在 JS 当中,要说“逻辑运算符”其实并不完全正确,Kyle Simpson 在《You Don't Know JS》系列书当中提到:“与其说是‘逻辑运算符',不如说是‘选择器运算符'。” 为什么大师要这样说呢?其实我们大多数人都被 JS 的表象给蒙蔽了,比如下面一段非常简单的代码:

if( "hello" && 0 ) {
 console.log(true);
 } else {
 console.log(false);
 }

如果你对 JS 了解的不够深刻,你可能会这样解释这段代码:首先在逻辑判断中,"hello" 是一个真值,0 是一个假值,一个真值和一个假值进行与运算,结果为 false 。这也可能是大多数人的理解,但其实不然,其内部的原理可不止这么简单,因为 && 和 || 返回的并不是判断条件的真假 ,而是判断条件中的一个原始值。它将依次对条件判断中的值进行判断,如果是非布尔值,则转换成布尔值做判断,然后再根据判断条件来决定返回哪一个值。

对于 && :该运算符返回条件语句中的第一个假值,如果所有的值都为真,则返回最后一个值,&& 也被称为 “守护运算符” 。比如下面一段代码:

var a = "hello" && "world";
 console.log(a); //world
 var b = 0 && 1;
 console.log(b); //0

可以看出,逻辑运算符其实返回的并不是条件的真假,而是原始值。如果条件语句中有多个 && 运算符,则一样遵循以上原则,从左向右依次判断,如果遇到了假值,就返回该假值,如果所有值都为真,则返回最后一个值。

对于 ||:该运算符与 && 运算符相反,它返回条件语句中的第一个真值,如果所有值都为假,则返回最后一个值。比如下面一段代码:

var a = "hello" || 0;
 console.log(a); //hello
 var b = 0 || NaN;
 console.log(b); //NaN

同样,|| 返回的也不是布尔值。如果有多个 || 则同样遵循相同的原则,从左向右依次扫描。

讲到这里也就来到了本篇文章的核心,在 JS 当中,条件判断语句都是建立在隐式转换之上的,也就是说所谓的逻辑运算符,实际上是在条件判断语句中从左向右依次扫描,如果是一个布尔值,则判断该布尔值的真假,如果是一个非布尔值,则先对该值进行隐式转换,然后再判断真假,如果满足条件,则返回该值,如果没有满足条件值,则返回最后一个值,然后在对返回的这个值做判断,如果是一个布尔值,则直接判断,如果是一个非布尔值,则先隐式转换成布尔值,再做判断。所以我们也可以把 && 称为 “取假运算符” ,把 || 称为 “取真运算符” ,因为这两个运算符的实质都是取条件语句中的第一个真值或者假值,如果始终没有找到,则返回最后一个值。而这样的算法也恰好满足逻辑判断的需求,比如 && 运算符,如果所有的值都是真值,那么返回哪个值其实都无所谓,因为所有值都能够被隐式转化为 true ,而只要有一个假值,则判断条件不成立,所以会返回第一个遇到的假值。而 || 运算符,如果所有的值都是假值,返回任意一个都会被隐式转换成 false ,但只要遇到了一个真值,则判断条件成立,所以会返回第一个遇到的真值。&& 和 || 运算符都是 “短路” 的。

所以我们可以自己实现一个逻辑运算的函数:

// && 等价于:
function AND () {
 for (var i = 0; i < arguments.length; i++) {
 if (!arguments[i]) {
  return arguments[i];
 }
 }
 return arguments[i-1];
}
// || 等价于:
function OR () {
 for (var i = 0; i < arguments.length; i++) {
 if(arguments[i]) {
  return arguments[i];
 }
 }
 return arguments[i-1];
}

(注:在这里我同时也想对 ! 这个运算符做讲解,但考虑到内容和篇幅的问题,暂时不做深入探究,仅做简单讲述。 ! 运算符实际上运行机制与 && 和 || 是一样的,首先会对参数值做判断,如果是一个布尔值,则进行取反运算,如果是一个非布尔值,则先进行隐式转换,再进行取反运算。而我们通常写的 if (something) 语句,实际上的意思 if (!!something))

然后我们可以这样使用:

var a = ["hello", undefined, "world"];
 console.log(AND.apply(null, a)); //undefined
 var b = ["", 0, NaN];
 console.log(OR.apply(null, b)); //NaN

进而,我们就可以推断出一下结论:

a = x || y;
//等价于:
a = x ? x : y;

a = x && y;
//等价于:
a = x ? y : x;

这通常也是一些压缩工具所做的事情,它们尽可能的将繁杂的条件判断语句转换成 && 或者 || ,因为这样代码更加的精简,但是可读性则就不那么可观了。

对于最开始的那一段代码:

if( "hello" && 0 ) {
 console.log(true);
 } else {
 console.log(false);
 }

我们现在就要这样解释:首先这是个与运算符,与运算符的作用是取第一个假值,如果所有的值都为真,那么则返回最后一个值。所以在这条语句中,第一个值是 "hello" ,因为该值是一个非布尔值,JS 引擎会先将它隐式转换成布尔值,而该值不在假值的范围内,所以会被转化成 true 。随后 JS 引擎会继续查找,第二个值是 0 ,该值同样也不是一个布尔值,所以 JS 引擎也会先将它隐式转换成布尔值,而该值在假值的范围内,所以会被转化成 false ,满足 && 运算符的查找条件,则将值 0 返回。而条件判断语句接受到了值 0 ,该值不是一个布尔类型的值,所以会先对它进行隐式转换,而该值在假值范围内,所以会被转化成 false ,然后控制台会输出 false。

所以说以后当我们看到 && 和 || 时候,就不要仅仅的从字面上的意义去理解了,在看完了这篇文章之后,你就可以很自豪很有底气的对别人说,你真的会用逻辑运算符吗?

好了,就这么两个小玩意居然背后也有着这么多的精髓,看来知识的深度是无穷的,冬季还真是一个能引发人们思考的季节。圣诞节即将到来,在这里提前预祝大家圣诞快乐,Merry Christmas!

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
[原创]后缀就扩展名为js的文件是什么文件
Dec 06 Javascript
JavaScript 解析读取XML文档 实例代码
Jul 07 Javascript
js中判断控件是否存在
Aug 25 Javascript
用js实现小球的自由移动代码
Apr 22 Javascript
jquery.ajax的url中传递中文乱码问题的解决方法
Feb 07 Javascript
jQuery获取cookie值及删除cookie用法实例
Apr 15 Javascript
jQuery实现的简单图片轮播效果完整示例
Feb 08 jQuery
element-ui的回调函数Events的用法详解
Oct 16 Javascript
浅谈Vue数据响应
Nov 05 Javascript
vue实现将一个数组内的相同数据进行合并
Nov 07 Javascript
node.js中path路径模块的使用方法实例分析
Feb 13 Javascript
Element实现表格嵌套、多个表格共用一个表头的方法
May 09 Javascript
js定时器实例分享
Dec 20 #Javascript
BootStrap Table 获取同行不同列元素的方法
Dec 19 #Javascript
Jquery Easyui进度条组件Progress使用详解(8)
Mar 26 #Javascript
详解Jquery的事件操作和文档操作
Dec 19 #Javascript
如何解决jQuery EasyUI 已打开Tab重新加载问题
Dec 19 #Javascript
详解用原生JavaScript实现jQuery的某些简单功能
Dec 19 #Javascript
Jquery Easyui对话框组件Dialog使用详解(14)
Dec 19 #Javascript
You might like
php启用sphinx全文搜索的实现方法
2014/12/24 PHP
php获取当月最后一天函数分享
2015/02/02 PHP
详解php中反射的应用
2016/03/15 PHP
Yii2框架中日志的使用方法分析
2017/05/22 PHP
ajax+php实现无刷新验证手机号的实例
2017/12/22 PHP
基于jquery的tab切换 js原理
2010/04/01 Javascript
jQuery.get、jQuery.getJSON、jQuery.post无法返回JSON问题的解决方法
2011/07/28 Javascript
关于event.cancelBubble和event.stopPropagation()的区别介绍
2011/12/11 Javascript
关于js数组去重的问题小结
2014/01/24 Javascript
jQuery scroll事件实现监控滚动条分页示例
2014/04/04 Javascript
javascript性能优化之DOM交互操作实例分析
2015/12/12 Javascript
js密码强度实时检测代码
2016/03/02 Javascript
正则表达式(语法篇推荐)
2016/06/24 Javascript
微信 java 实现js-sdk 图片上传下载完整流程
2016/10/21 Javascript
require、backbone等重构手机图片查看器
2016/11/17 Javascript
jQuery事件绑定方法学习总结(推荐)
2016/11/21 Javascript
原生js实现网页顶部自动下拉/收缩广告效果
2017/01/20 Javascript
Angular5中提取公共组件之radio list的实例代码
2018/07/10 Javascript
小程序云开发初探(小结)
2018/10/24 Javascript
VScode格式化ESlint方法(最全最好用方法)
2019/09/10 Javascript
Jquery cookie插件实现原理代码解析
2020/08/04 jQuery
python3.5仿微软计算器程序
2020/03/30 Python
利用python批量修改word文件名的方法示例
2017/10/17 Python
python实现发送邮件功能代码
2017/12/14 Python
python sorted函数的小练习及解答
2019/09/18 Python
Tensorflow 模型转换 .pb convert to .lite实例
2020/02/12 Python
python logging模块的使用
2020/09/07 Python
Bowflex美国官方网站:高级家庭健身器材
2017/12/22 全球购物
菲律宾优惠券网站:MetroDeal
2019/04/12 全球购物
什么是Remote Module
2016/06/10 面试题
研究生自我鉴定范文
2013/10/30 职场文书
优秀大学生推荐信范文
2013/11/28 职场文书
2015年大学生党员承诺书
2015/04/27 职场文书
MySQL查询学习之基础查询操作
2021/05/08 MySQL
解决xampp安装后Apache无法启动
2022/03/21 Servers
讲解MySQL增删改操作
2022/05/06 MySQL