深入了解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 相关文章推荐
JQuery 表单中textarea字数限制实现代码
Dec 07 Javascript
推荐30个新鲜出炉的精美 jQuery 效果
Mar 26 Javascript
javascript几个易错点记录
Nov 26 Javascript
JS逆序遍历实现代码
Dec 02 Javascript
jQuery 移动端artEditor富文本编辑器
Jan 11 Javascript
JS实现动态生成表格并提交表格数据向后端
Nov 25 Javascript
canvas学习之API整理笔记(二)
Dec 29 Javascript
JS对象是否拥有某属性如何判断
Feb 03 Javascript
webpack组织模块打包Library的原理及实现
Mar 10 Javascript
值得收藏的八个常用的js正则表达式
Oct 19 Javascript
浅谈js中的bind
Mar 18 Javascript
vue中组件通信详解(父子组件, 爷孙组件, 兄弟组件)
Jul 27 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
BBS(php &amp; mysql)完整版(一)
2006/10/09 PHP
浅析PHP安装扩展mcrypt以及相关依赖项(PHP安装PECL扩展的方法)
2013/07/05 PHP
php中使用key,value,current,next和prev函数遍历数组的方法
2015/03/17 PHP
PHP预定义变量9大超全局数组用法详解
2016/04/23 PHP
PHP将页面中点击数量高的链接进行高亮显示的方法
2016/05/30 PHP
Jquery 滑入滑出效果实现代码
2010/03/27 Javascript
jquery png 透明解决方案(推荐)
2010/08/21 Javascript
AngularJS动态加载模块和依赖的方法分析
2016/11/08 Javascript
js/jq仿window文件夹移动/剪切/复制等操作代码
2017/03/08 Javascript
Three.js入门之hello world以及如何绘制线
2017/09/25 Javascript
分析JS中this引发的bug
2017/12/12 Javascript
使用live-server快速搭建本地服务器+自动刷新的方法
2018/03/09 Javascript
jQuery实现模糊搜索功能的方法分析
2018/06/29 jQuery
js+css实现红包雨效果
2018/07/12 Javascript
解决vue+element 键盘回车事件导致页面刷新的问题
2018/08/25 Javascript
jquery弹窗时禁止body滚动条滚动的例子
2019/09/21 jQuery
JavaScript switch语句使用方法简介
2019/12/30 Javascript
js实现mp3录音通过websocket实时传送+简易波形图效果
2020/06/12 Javascript
vue 动态组件(component :is) 和 dom元素限制(is)用法说明
2020/09/04 Javascript
python中 chr unichr ord函数的实例详解
2017/08/06 Python
django中的setting最佳配置小结
2017/11/21 Python
对python模块中多个类的用法详解
2019/01/10 Python
浅析Python与Mongodb数据库之间的操作方法
2019/07/01 Python
python使用多线程查询数据库的实现示例
2020/08/17 Python
Python更改pip镜像源的方法示例
2020/12/01 Python
CSS3 实现图形下落动画效果
2020/11/13 HTML / CSS
HTML5实现多张图片上传功能
2016/03/11 HTML / CSS
HTML5中的强制下载属性download使用实例解析
2016/05/12 HTML / CSS
加拿大建筑和装修专家:Reno-Depot
2017/12/21 全球购物
党校自我鉴定范文
2013/10/02 职场文书
专科毕业生自我鉴定
2013/12/01 职场文书
人事主管岗位职责范本
2013/12/04 职场文书
运动会100米解说词
2014/01/23 职场文书
2014高考励志标语
2014/06/05 职场文书
云南省召开党的群众路线教育实践活动总结会议新闻稿
2014/10/21 职场文书
总经理致辞
2015/07/29 职场文书