一、概念
与其他语言不同,在js中,逻辑运算符可以返回任何类型的数据,不仅仅是true和false。
&&和||的返回值是两个操作数的其中一个。即a&&b或者a||b返回的是要么是a,要么是b,而其他语言中返回的是true or false。
在js逻辑运算中,需要隐式的转换为boolean类型再来运算,转换规则为:
1. 对象、非零number、非空string——>true
2. 0、""、null、false、undefined、NaN——>false
具体见下表:
数据类型 | 转换为boolean后的值 |
NAN | FALSE |
null | FALSE |
undefined | FALSE |
Object | TRUE |
Function | TRUE |
0 | FALSE |
非零的数字 | TRUE |
""(空字符串) | FALSE |
非空字符串 | TRUE |
因此:
a && b : 将a, b转换为Boolean类型, 再执行逻辑与, 如果结果是true返回b, false返回a
a || b : 将a, b转换为Boolean类型, 再执行逻辑或, 如果结果是false返回b, true返回第一个为true的值。
逻辑&&和逻辑||还有一个短路原则:知道了前面第一个的结果就知道最后的输出
a&&b:左操作数为假值时,返回左操作数,否则返回右操作数。
a||b:左操作数为假值时,返回右操作数,否则返回左操作数。
只要“||”前面为false,不管“||”后面是true还是false,都返回“||”后面的值。
只要“||”前面为true,不管“||”后面是true还是false,都返回“||”前面的值。
只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返“&&”前面的值;
只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返“&&”后面的值;
同样对于多个操作数的情况:
a||b||c||d:若结果为true则返回第一个true值,若结果为false则返回最后一个操作数。eg:var a = “” || null || 3 || 2 -> var a = fasel || false || true || true 结果为true 则返回第一个true,即是3
a&&b&&c&&d:若结果为false则返回第一个false,若结果为true则返回最后一个操作数。eg:var b = 2&&null&&1&&0 -> var b = true&&false&&true&&false结果是false 则返回第一个false 即是null
二、理解
“&&”和“||”运算符均可以从三个不同的层次来逐步理解:
以“&&”为例解释如下:
(1)最简单的第一层理解是,当操作数都是布尔值的时候,“&&”对两个值执行布尔与(AND)操作,只有在第一个操作数和第二个操作数都是true的时候,它才返回true。如果其中一个操作数是false,它返回false。
(2)“&&”的操作数并不一定是布尔值。于是对“&&”的第二层理解是,“&&”可以对真值和假值进行布尔与(AND)操作。如果两个操作数都是真值,那么返回一个真值;否则,至少一个操作数是假值的话,则返回一个假值。在JavaScript中任何希望使用布尔值的地方,表达式和语句都会将其当做真值或假值来对待,因此实际上“&&”并不总是返回true和false。
(3)第三层理解,运算符首先计算左操作数的值,即首先计算“&&”左侧的表达式。如果计算结果是假值,那么整个表达式的结果一定也是假值,因此“&&”这时简单地返回左操作数的值,而并不会对右操作数进行计算。反过来讲,如果左操作数是真值,那么整个表达式的结果则依赖于右操作数的值。如果右操作数是真值,那么整个表达式的值一定是真值;如果右操作数是假值,那么整个表达式的值一定是假值。
理解如下例子对于理解“&&”可能不会去计算右操作数的情况至关重要,在以下示例代码中,变量p的值是null,而如果计算表达式p.x的话则会抛出一个类型错误异常。但是示例代码使用了“&&”的一种符合语言习惯的用法,因此只有在p为真值(不能是null或者undefined)的情况下才会计算p.x。反过来,我们一般需要用某个对象p中的某个元素时,为了严谨也应该先对其是否存在进行判断。
var o = { x : 1 }; var p = null; o && o.x // =>1:o 是真值,因此返回值为o.x p && p.x //=>null: p是假值,因此将其返回,而并不去计算p.x
三、使用场景
1.||运算符最常用的方式是用来从一组备选表达式中选出第一个真值表达式,即从一系列值中获取第一个true值。
// 如果max_width已经定义了,直接使用它;否则在preferences对象中查找max_width;如果没有定义它,则使用一个写死的常量 var max = max_width || preferences.max_width || 500;
2.判断某个元素是否存在时:if(attr)写成if(!!attr)更加严谨。
!!的作用是把一个其他类型的变量转成的bool类型。eg:
typeof 5//"number" typeof !!5//"boolean"
3.对函数中的参数赋以默认值:a=a||
"defaultValue"
;
例如当a不存在或不合法时为其赋默认值,比起如下两种写法:
if(!a){ a="defaultValue"; }
if(a==null||a==""||a==undefined){ a="defaultValue"; }
用||一句话就可以实现,简单又优雅:
a=a||"defaultValue";
4.利用&&的短路特性有条件的执行代码
(1)在回调中,经常这样写:callback&&callback(),先判断 callback 是不是存在,存在才执行,这样写更加严谨,如果直接写 callback(); 当callback不存在时代码就会报错。
(2)又或者常见的条件语句:if
(a==b) stop();如果用&&来实现即为:
(a==b)&&stop();
(3)判断某个对象存在再取值:p && p.x
应用举例:
如图:
假设对成长速度显示规定如下:
成长速度为5显示1个箭头;
成长速度为10显示2个箭头;
成长速度为12显示3个箭头;
成长速度为15显示4个箭头;
其他都显示都显示0个箭头。
(1)最容易想到也最常规的方法:if-else
var add_level = 0; if(add_step == 5){ add_level = 1; } else if(add_step == 10){ add_level = 2; } else if(add_step == 12){ add_level = 3; } else if(add_step == 15){ add_level = 4; } else { add_level = 0; }
(2)switch方法:
var add_level = 0; switch(add_step){ case 5 : add_level = 1; break; case 10 : add_level = 2; break; case 12 : add_level = 3; break; case 15 : add_level = 4; break; default : add_level = 0; break; }
(3)而如果用&&和||就会超级简单:
var add_level = (add_step==5 && 1) || (add_step==10 && 2) || (add_step==12 && 3) || (add_step==15 && 4) || 0;
优化为:
var add_level={'5':1,'10':2,'12':3,'15':4}[add_step] || 0; //对象取值有两种方式,一种object.key,一种是object[key],此处为法二
特别是如果需求改成:
成长速度为>12显示4个箭头;
成长速度为>10显示3个箭头;
成长速度为>5显示2个箭头;
成长速度为>0显示1个箭头;
成长速度为<=0显示0个箭头。
那么用switch实现起来也很麻烦了,而同样用&&和||只要如下即可。
var add_level = (add_step>12 && 4) || (add_step>10 && 3) || (add_step>5 && 2) || (add_step>0 && 1) || 0;
注:与&&和||不同,!操作符的行为与C、Java等语言是一致的,只返回boolean值(true或false)。