首页 > 代码库 > 逻辑运算符& |

逻辑运算符& |

  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。

  

 

逻辑运算符& |