在平时的开发中,咱们常常会遇到多种运算符在同一个表达式中出现的状况,尤为是在三元条件判断运算符中。javascript
三元条件判断运算符虽然可让咱们避免写过多的if...else条件判断,但多层三元运算符嵌套,其中又包含其余不一样优先级的运算符时,对于阅读咱们代码的人来讲,简直就是噩梦。java
今天咱们就结合一个现实中常常用到的工具函数 isEmpty() 的实现,来说解一下如何解读复杂的运算符嵌套jquery
isEmpty 是 著名的 loadsh 库提供的一个工具方法,被应用于断定一个javascript 对象是否为空对象。segmentfault
对于空对象,loadsh 是这么解释的:数组
若是【对象没有本身的可枚举字符串键控属性】,则认为它们是空的, 若是参数、对象、缓冲区、字符串或相似于jquery的集合等【相似数组的值的长度为0】,则认为它们为空。 相似地,若是【映射和集合的大小为0】,则认为它们是空的
isEmpty = function (val) { return !(!!val ? typeof val === 'object' ? Array.isArray(val) ? !!val.length : !!Object.keys(val).length : true : false); }
javascript 的运算符优先级能够参考MDN上的说明,以下图:框架
咱们再看内部实现代码,其中val
为要判断是否为空对象的值:函数
return !(!!val ? typeof val === 'object' ? Array.isArray(val) ? !!val.length : !!Object.keys(val).length : true : false);
如今根据运算符优先级一步一步解读运算过程工具
return
后面应该是一个表达式的值,咱们假定这个值为X,则整个表达式能够看作:var X = !(...); return X;
!(...)
X = !Y
Y = !!val ? typeof val === 'object' ? Array.isArray(val) ? !!val.length : !!Object.keys(val).length :true :false
Y = !!val ? typeof val === 'object' ? (true/false) ? !!(0/1/2/.../N) : !!(0/1/2/.../N) :true :false
由于咱们这里对val
的值不必定,因此这里对 Array.isArray(val)
和 Object.keys(val).length
最终的计算结果有多种可能,我用/
号隔开了各类可能值,而且将他们放入同一个括号内,若是是正式的计算,咱们传入的val
是一个肯定的值,那么这些运算结果也会是一个肯定值,而且也不会有括号。spa
typeof
运算符的优先级都是16,是剩余运算符中最高的,因此对这两种进行运算,结果以下:Y = (true/false) ? (string/object/boolean/null/undefined)==='object' ? (true/false) ? (true/false) : (true/false) : true :false
这个地方比较绕,由于!!
会将后面的值强制转换为布尔值,因此最后的结果几乎都是由 true
或false
组成的了。code
Y = (true/false) ?(true/false) ? (true/false) ? (true/false) : (true/false) : true :false
结合性
了,咱们发现条件运算符的结合性是从右至左,那么咱们的表达式就变成了:Y = (true/false) ? ... : false
咱们回溯如下这里面的 (true/false)
其实就是原始表达式中 !!val
的运算结果,然而这里还没法运算出整个表达式的结果,由于 ...
所表明的那部分还不是一个最终值,还须要运算,记得最开始的作作法吗?对于!(...)
这个表达式,咱们将括号内的表达式用Y
来代替了,一样地,咱们把这里 ...
所表明的表达式部分用一个字母M
来表明,即:
M = (true/false) ? (true/false) ? (true/false) : (true/false) : true; Y = (true/false) ? M : false;
到这里,计算机就开始判断了:
!!val
的值为false
,则直接返回 false
(即括号后面的值),后面的M中的表达式就再也不运算了。那么此时 Y=false
, 而 !Y
至关于取反,X = !Y
的值就等于true
。咱们这个方法是用来判断是否为空对象的,返回结果为true
,就说明这个val
是空对象。!!val === false
的 val
都有哪些呢? 0
,""
,false
,null
,undefined
符合这个特征,咱们发发现,它们都是javascript中的‘假值’。!!val
的值为 true
呢,则须要返回M表达式的结果,咱们就须要继续计算M表达式的值了。M
表达式的运算过程,依葫芦画瓢,咱们能够获得:M = (true/fasle) ? ... : true
经过回溯,咱们能够知道,这里的(true/false)
其实就是原始表达式中的typeof val === 'object'
的最终运算结果。
一样的,咱们将 ...
内的内容使用字母 N
代替,结果以下:
N = (true/fasle) ? (true/fasle) : (true/false) M = (true/false) ? N : true;
到这里,计算机又开始判断:
typeof val === 'object'
的值为false
, 即说明val
不是对象类型,则直接返回true
(冒号后面的值),不须要再运算N表达式的结果。此时 Y = true, 则 X= !Y=false, 最终值为false
,说明val
不是空对象。typeof val === 'object'
的值为 true
则须要返回N表达式的值做为结果,计算机须要计算运算N表达式的值。N
表达式,其中有三个布尔值,经过回溯,咱们也能够知道他们的原始表达式分别是:Array.isArray(val)
!!val.length
!!Object.keys(val).length
那么咱们知道,这一步当val
为对象类型时,则须要判断它是数组仍是非数组:
若是是数组,则拿到数组的长度值,对长度值作!!
操做
0
,则操做结果为false
, 返回后,Y=false,X=!Y=true,说明 长度为0的数组为空对象true
,将结果返回后,Y=true, X=!Y=false,说明长度大于0的数组不属于空对象若是不是数组,则取它的可枚举属性的长度(Object.keys(val).length
),并对长度作!!
操做
0
,则操做结果为false
, 返回后,Y=false,X=!Y=true,说明 可枚举属性长度(个数)为0的对象为空对象true
,将结果返回后,Y=true, X=!Y=false,说明可枚举属性长度大于0的对象不属于空对象至此,咱们按照程序执行的顺序步进似的完成了整个运算过程的模拟,咱们学到了如下几点:
理解运算过程对咱们理解整个程序的实现逻辑和做者的思惟方式相当重要,但愿以上分析过程能够在你们阅读知名框架中大神级代码时对你们有所帮助。
本文由博客一文多发平台 OpenWrite 发布!