深入了解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 窗口抖动示例
Sep 04 Javascript
解析URI与URL之间的区别与联系
Nov 22 Javascript
简单js代码实现selece二级联动(推荐)
Feb 18 Javascript
jquery实现先淡出再折叠收起的动画效果
Aug 07 Javascript
JavaScript中的定时器之Item23的合理使用
Oct 30 Javascript
JS前端笔试题分析
Dec 19 Javascript
JavaScript实现数组降维详解
Jan 05 Javascript
利用jQuery实现滑动开关按钮效果(附demo源码下载)
Feb 07 Javascript
ionic3+Angular4实现接口请求及本地json文件读取示例
Oct 11 Javascript
JS表单传值和URL编码转换
Mar 03 Javascript
通过cordova将vue项目打包为webapp的方法
Feb 02 Javascript
vue打开子组件弹窗都刷新功能的实现
Sep 21 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
不用mod_rewrite直接用php实现伪静态化页面代码
2008/10/04 PHP
smarty巧妙处理iframe中内容页的代码
2012/03/07 PHP
一个PHP针对数字的加密解密类
2014/03/20 PHP
php实现获取及设置用户访问页面语言类
2014/09/24 PHP
php防止用户重复提交表单
2015/11/02 PHP
PHP调试及性能分析工具Xdebug详解
2017/02/09 PHP
jquery插件制作 表单验证实现代码
2012/08/17 Javascript
javascript游戏开发之《三国志曹操传》零部件开发(二)人物行走的实现
2013/01/23 Javascript
jsp js鼠标移动到指定区域显示选项卡离开时隐藏示例
2013/06/14 Javascript
jQuery中parentsUntil()方法用法实例
2015/01/07 Javascript
JavaScript中Function函数与Object对象的关系
2015/12/17 Javascript
基于Vue.js的表格分页组件
2016/05/22 Javascript
jQuery获取多种input值的简单实现方法
2016/06/20 Javascript
jQuery代码实现实时获取时间
2017/01/29 Javascript
Vuex利用state保存新闻数据实例
2017/06/28 Javascript
Vue添加请求拦截器及vue-resource 拦截器使用
2017/11/23 Javascript
vue-cli+webpack项目 修改项目名称的方法
2018/02/28 Javascript
React手稿之 React-Saga的详解
2018/11/12 Javascript
javascrpt密码强度校验函数详解
2020/03/18 Javascript
js实现时间日期校验
2020/05/26 Javascript
vue自定义树状结构图的实现方法
2020/10/18 Javascript
[05:03]显微镜下的DOTA2第十期——Ti3豪之超神幽鬼
2014/06/23 DOTA
python实现类似ftp传输文件的网络程序示例
2014/04/08 Python
9种python web 程序的部署方式小结
2014/06/30 Python
Python自定义一个类实现字典dict功能的方法
2019/01/19 Python
对Python3 * 和 ** 运算符详解
2019/02/16 Python
python爬取内容存入Excel实例
2019/02/20 Python
kali中python版本的切换方法
2019/07/11 Python
Django 博客实现简单的全文搜索的示例代码
2020/02/17 Python
世界上最好的旅行夹克:BauBax
2018/12/23 全球购物
协议书样本
2014/04/23 职场文书
小学关爱留守儿童活动方案
2014/08/25 职场文书
党员批评与自我批评发言稿
2014/10/14 职场文书
背起爸爸上学观后感
2015/06/08 职场文书
MIME类型中application/xml与text/xml的区别介绍
2022/01/18 HTML / CSS
python实现一个简单的贪吃蛇游戏附代码
2022/06/28 Python