首页 > 代码库 > 逻辑运算符& |
逻辑运算符& |
JS在做逻辑运算的时候会自动将非布尔类型的值进行隐式转换,转换成布尔类型的值然后再进行逻辑运算。
在初学JS的时候,都会讲到在隐式转换中,除了几个特定的假值,其他的均会转换成真值,这些假值有:
1 NaN; 2 ""; 3 undefined; 4 null; 5 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。这也可能是大多数人的理解,但其实不然,其内部原理可没有这么简单,因为&&和||返回值并不是判断条件的真假,而是判断条件中的一个原始值。它将依次对条件判断中的值进行判断,如果是布尔值,则转换成布尔值做判断,然后再根据判断条件来决定返回哪一个值。
对于&&:该运算符返回条件语句中的第一个假值,如果所有的值都为真,则返回最后一个值,&&也被称作“守护运算符”。比如下面一段代码:
1 var a = "hello" && "world"; 2 console.log(a); //world 3 var b = 0 && 1; 4 console.log(b); //0
可以看出,逻辑运算符其实返回的并不是条件的真假,而是原始值。如果条件语句中有多个&&运算符,则一样遵循以上原则,从左想右依次判断,如果遇到假值,就返回该假值,如果所有值都为真,则返回最后一个值。
对于||:该运算符与&&运算符相反,它返回条件语句中的第一个真值,如果所有值都为假,则返回最后一个值。比如下面一段代码:
1 var a = "hello" || 0; 2 console.log(a); //hello 3 var b = 0 || NaN; 4 console.log(b); //NaN
同样,||返回的也不是布尔值。如果有多个||则同样遵循相同的原则,从左到右依次扫描。
在JS当中,条件判断语句都是建立的隐式转换之上的,也就是说所谓的逻辑运算符,实际上是在条件判断语句中从左向右依次扫描,如果是一个布尔值,则判断该布尔值的真假,如果是一个非布尔值,则先对该值进行隐式转换,然后再判断真假,如果满足条件,则返回该值,如果没有满足条件值,则返回最后一个值,然后再对返回的这个值做判断,如果是一个布尔值,则直接判断,如果是一个非布尔值,则先对该值进行隐式转换,再做判断。
所以我们也可以把&&叫做“取假运算符”,把||叫做“取真运算符”,因为这两个运算符的实质都是取条件语句中的第一个真值或者假值,如果始终没找到,则返回最后一个值。而这样的算法也恰好满足逻辑判断的需求,比如&&运算符,如果所有值都是真值,那么返回哪个值其实都无所谓,因为所有值都能被隐式转化为true,而只要有一个假值,则判断条件不成立,所以会返回第一个遇到的假值。而||运算符,如果遇到的所有值都是假值,返回任意一个都能够隐式转换成false,但只要遇到一个真值,则判断条件成立,所以会返回第一个遇到的真值。&&和||运算符都是“短路”的。
所以我们自己实现一个逻辑运算的函数:
1 //&&等价于 2 function ADN(){ 3 for(var i = 0; i < arguments.length; i++){ 4 if(!arguments[i]){ 5 return arguments[i]; 6 } 7 return arguments[i-1]; 8 } 9 } 10 //||等价于 11 function OR(){ 12 for(var i = 0; i < arguments.length; i++){ 13 if(arguments[i]){ 14 return arguments[i]; 15 } 16 return arguments[i-1]; 17 } 18 }
运算符实际上运行机制与&&和||是一样的,首先会对参数值做判断,如果是一个布尔值,则进行取反运算,如果是一个非布尔值,则先进行隐式转换,再进行取反运算。而我们通常写的if(something)语句,实际上的意思是if(!!something)
然后我们可以这样使用:
1 var a = ["hello", undefined, "world"]; 2 console.log(AND.apply(null, a)); //undefined 3 var b = ["", 0, NaN]; 4 console.log(OR.apply(null, b)); //NaN
进而,我们可以推断出以下结论:
1 a = x || y; 2 //等价于 3 a = x ? x : y; 4 5 a = x && y; 6 //等价于 7 a = x ? y : x;
这通常也是一些压缩工具所做的事,他们尽可能将繁杂的条件判断语句转换成&&和||,因为这样代码更加精简,但是可读性就不那么可观了。
对于最可是的那一段代码:
1 if("hello" && 0){ 2 console.log(true); 3 }else{ 4 console.log(false); 5 }
我们现在就要这样解释:首先这是个与运算符,与运算符的作用是取第一个假值,如果所有值都为真,那么则返回最后一个值。所以这条语句中,第一个值是“hello”,是一个非布尔值,JS引擎会先将它隐式转换成布尔值,而该值不在假值的范围内,所以会被转换成true。随后,JS引擎会继续查找,第二个值是0,该值同样不是一个布尔值,所以JS引擎也会先将它进行隐式转换成布尔值,而该值在假值的范围内,所以会被转化成false,满足&&运算符的查找条件,则将值0返回。而条件判断语句接收到了值0,该值不是一个布尔类型的值,所以会对它进行隐式转换,而该值在假值的范围内,所以会被转换成false,然后控制台会输出false。
逻辑运算符& |