9月阅读周·JavaScript权威指南:表达式和运算符,逻辑表达式篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读八个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》。
当前阅读周书籍:《JavaScript权威指南》。
逻辑表达式
逻辑与(&&)
“&&”运算符可以从三个不同的层次进行理解。最简单的第一层理解是,当操作数都是布尔值的时候,“&&”对两个值执行布尔与(AND)操作,只有在第一个操作数和第二个操作数都是true的时候,它才返回true。如果其中一个操作数是false,它返回false。
“&&”常用来连接两个关系表达式:
x==0&&y==0//只有在x和y都是0的时候,才返回true
关系表达式的运算结果总是为true或false,因此当这样使用的时候,“&&”运算符本身也返回true或false。关系运算符的优先级比“&&”(和“||”)要高,因此类似这种表达式可以放心地书写,而不用补充圆括号。
但是“&&”的操作数并不一定是布尔值,回想一下,有些值可以当做“真值”和“假值”(假值是false、null、undefined、0、-0、NaN和"",所有其他的值包括所有对象都是真值)。对“&&”的第二层理解是,“&&”可以对真值和假值进行布尔与(AND)操作。如果两个操作数都是真值,那么返回一个真值;否则,至少一个操作数是假值的话,则返回一个假值。在JavaScript中任何希望使用布尔值的地方,表达式和语句都会将其当做真值或假值来对待,因此实际上“&&”并不总是返回true和false,但也并无大碍。
运算符返回一个“真值”或者“假值”,但并没有说明这个“真值”或者“假值”到底是什么值。为此,我们深入讨论对“&&”的第三层(也是最后一层)理解。运算符首先计算左操作数的值,即首先计算“&&”左侧的表达式。如果计算结果是假值,那么整个表达式的结果一定也是假值,因此“&&”这时简单地返回左操作数的值,而并不会对右操作数进行计算。
反过来讲,如果左操作数是真值,那么整个表达式的结果则依赖于右操作数的值。如果右操作数是真值,那么整个表达式的值一定是真值;如果右操作数是假值,那么整个表达式的值一定是假值。因此,当左操作数是真值时,“&&”运算符将计算右操作数的值并将其返回作为整个表达式的计算结果:
var o={x:1};
var p=null;
o&&o.x//=>1:o是真值,因此返回值为o.x
p&&p.x//=>null:p是假值,因此将其返回,而并不去计算p.x
这对于理解“&&”可能不会去计算右操作数的情况至关重要,在上述示例代码中,变量p的值是null,而如果计算表达式p.x的话则会抛出一个类型错误异常。但是示例代码使用了“&&”的一种符合语言习惯的用法,因此只有在p为真值(不能是null或者undefined)的情况下才会计算p.x。
“&&”的行为有时称做“短路”(short circuiting),我们也会经常看到很多代码利用了这一特性来有条件地执行代码。例如,下面两行JavaScript代码是完全等价的:
if(a==b)stop();//只有在a==b的时候才调用stop()
(a==b)&&stop();//同上
一般来讲,当“&&”右侧的表达式具有副作用的时候(赋值、递增、递减和函数调用表达式)要格外小心。因为这些带有副作用的表达式的执行依赖于左操作数的计算结果。
尽管“&&”可以按照第二层和第三层的理解进行一些复杂表达式运算,但大多数情况下,“&&”仅用来对真值和假值做布尔计算。
逻辑或(||)
“||”运算符对两个操作数做布尔或(OR)运算。如果其中一个或者两个操作数是真值,它返回一个真值。如果两个操作数都是假值,它返回一个假值。
尽管“||”运算符大多数情况下只是做简单布尔或(OR)运算,和“&&”一样,它也具有一些更复杂的行为。它会首先计算第一个操作数的值,也就是说会首先计算左侧的表达式。如果计算结果为真值,那么返回这个真值。否则,再计算第二个操作数的值,即计算右侧的表达式,并返回这个表达式的计算结果。
和“&&”运算符一样,同样应当避免右操作数包含一些具有副作用的表达式,除非你目地明确地在右侧使用带副作用的表达式,而有可能不会计算右侧的表达式。
这个运算符最常用的方式是用来从一组备选表达式中选出第一个真值表达式:
//如果max_width已经定义了,直接使用它;否则在preferences对象中查找max_width
//如果没有定义它,则使用一个写死的常量
var max=max_width||preferences.max_width||500;
这种惯用法通常用在函数体内,用来给参数提供默认值:
//将o的成员属性复制到p,并返回p
function copy(o,p){
p=p||{};//如果向参数p没有传入任何对象,则使用一个新创建的对象
//函数体内的主逻辑
}
逻辑非(!)
“!”运算符是一元运算符。它放置在一个单独的操作数之前。它的目的是将操作数的布尔值进行求反。例如,如果x是真值,则!x返回false;如果x是假值,则!x返回true。
和“&&”与“||”运算符不同,“!”运算符首先将其操作数转换为布尔值,然后再对布尔值求反。也就是说“!”总是返回true或者false,并且,可以通过使用两次逻辑非运算来得到一个值的等价布尔值:!!x。
作为一个一元运算符,“!”具有很高的优先级,并且和操作数紧密绑定在一起。如果你希望对类似p&&q的表达式做求反操作,则需要使用圆括号:!(p&&q)。布尔计算的更多原理性知识不必要做过多的解释,这里仅用JavaScript代码做简单说明:
//对于p和q取任意值,这两个等式都永远成立
!(p&&q)===!p||!q
!(p||q)===!p&&!q
总结
逻辑运算符“&&”、“||”和“!”是对操作数进行布尔算术运算,经常和关系运算符一起配合使用,逻辑运算符将多个关系表达式组合起来组成一个更复杂的表达式。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)