这篇主要说三类操作符的使用,分别是 判等操作符 比较操作符 运算符,建议先看一下我的上一篇《JavaScript 类型转换全面总结》保证你对js中类型的转换有充分的了解。
开篇先说一下在学习操作符和类型转换中get到的一些体会 "请不要 相信直觉!" 了解清楚每个操作符的运算比较流程,而不要凭借"语感"来作出判断!
判等 == & ===
首先我们来说一下判等运算符的使用,我们知道js中判断相等可以使用 == 和 === ,他们的区别是
- == 允许在比较的同时进行类型转换,如果比较双方类型不同,会先尝试转成相同的类型,再进行判断
- === 不允许比较的双方在比较时类型转换,类型不同会直接判定为不同
== 的判定会相对宽松一些,其判定的规则可以主要归纳为:
1. 如果双方都是复杂类型,则判断双方的地址是否一致 如 {} != {} 因为两个{}是两个不同的地址
2. 如果双方都是简单类型,并且类型相同,则可以直接判断其是否相等
3.如果双方都是简单类型,并且类型不相同,则都转换为数字类型进行比较 如 '1' == true 为true 即: ‘1’ -> 1 true-> 1
4. 如果有一方是复杂类型,则先求复杂类型的原始值[[Primitive]] 在得到原始值之后再按照简单类型比较
5. NaN和任何值都不等,即使两个NaN也不相等,任何两个Symbol也都不相等,也不等于任何值
6. null == undefined 但是不等于这俩以外的任何值
对于 === 则简单得多
1. 对于类型不同的,直接不等
2. 对于两个复杂类型,则判断其地址是否相等
3. NaN 不全等于任何值,Symbol也不全等于任何值
4. undefined和null算是两个不同的类型,互相不相等
我们可以利用这个特性 如
if( a == null ) 或者 if( a == undefined )
这样 a为null或者undefined的时候,都可以被检测到!
下面我们看一些例子运用上面的规则:
null == undefined // true null和undefined默认相等
'NaN' == NaN //false NaN和任何值都不相等
5 == NaN // false NaN和任何值都不相等
NaN == NaN //false NaN和任何值都不相等
NaN != NaN // true
false == 0 // true 同为简单类型,且类型不同,都转换为数字类型比较 false->0 为true
true == 1 // true 原理同上
true == 2 // false 因为true转成数字为1
这里需要注意,我们在代码中判断一个值是不是为真时,不要用以下的方式
if( a == true ) { ... }
因为如果a是1以外的数字,这个判断都是为false的,而我们需要的是,0判断为false,其他值都为true。
我们可以使用 if(!!a) 或者 if(a) if(Boolean(a))等方式!
undefined == 0 // false undefined 不等于null以外的任何值
null == 0 // false null不等于undefined以外的任何值
'5' == 5 // true 不同类型的简单类型比较,都转换为数字 5 ==5 为真
var a = 42
var b = [42]
a == b // true
当比较双方有一方是复杂类型时,会先调用其[[Primitive]]求原始值,[42]的toString方法返回"42" 其原始值为"42" 就转变成了 42 == "42" 都转换成数字比较 为true
var a = "abc"
var b = new String(a)
a == b //true
a === b // false
a为简单类型 b为a的包装对象,为复杂类型,所以a === b自然为false
b为复杂类型,a==b比较时,先调[[Primitive]] 求b的原始值 包装对象的valueOf值为"abc" 所以a==b
我们称包装对象的.valueOf() 为"拆封"
var a = null
var b = new Object()
a == b //false
b为复杂类型,求原始值为"[object Object]" 不是null或者undefined 所以为false
var c = undefined
var d = new Object()
c == d //false 和上面同理
var e = NaN
var f = new Number(e)
e == f //false
f为包装对象,其开封后值为NaN 但是NaN和任何值都不等 另外一个NaN也不行,所以为false
[] == ![] // true
首先 ![] 转成false []==false , []是复杂类型,其原始值为"" 转变成 "" == false 类型不同都转成数字 即 0 == 0 true
2 == [2] //true [2]原始值为 "2" 2=="2" true
"" == [null] //true [null]的原始值是 "" 不是 "null" 这里不明白的可以看我上一篇文章,简单来说就是null是数组中的占位符,toString函数会跳过null,同样被跳过的还有undefiend
"true" == true //false 这个就是凭借直觉很容易错的,由于两个比较的值都是简单类型,则转换成数字类型 "true"->NaN NaN和任意值不等 为false
"foo" == ["foo"] 这个不用多说了 true
是不是晕了?? 如果晕了 请多看几遍,对照着最上面 == 和 === 比较的规则,忘掉脑中的直觉。多试几次!
比较 > < >= <=
下面看和比较有关的操作符,即 >, <, >=, <=
比较的规则比较简单
1. 如果比较的双方都是字符串,则按照ASCII的顺序逐个字符比较大小
2. 如果比较的双方都是简单类型,则转换成数字比较
3. 如果比较多双方存在复杂类型,则先转换成原始值,在根据简单类型的规则进行比较!
4. NaN 不能和任何值比大小
5. null和undefined可以转换成数字比较大小
看一些例子:
console.log(null>-1);//true null-> 0
console.log(null>=-1);//true null->0
console.log(null<1);//true null->0
console.log(null<=1);//true null->0
console.log(null==0);//false. null和undefined无法类型转换
console.log(null===0);//false 类型不同 不等
console.log(null!=0);//true null和undefined无法类型转换
console.log(null!==0);//true 类型不同 不等
注意! 等号 == / === 需要按照相等操作的规则判断
这里注意一个很容易错的点:
undefined -> NaN null ->0 之后比较只存在于 比较符 即可 > < >= <= 中,可以这么转换,但是在 == 或 === 中 这俩值不等于他们以外的任何值,需要按照判等的规则看!
console.log('2' > 10);//false
都转成数字类型比较 2<10
console.log('2' > '10');//true
都是字符串,按照每位的ASCII比较,其中'2'的ASCII>'1'的ASCII
console.log('10'.charCodeAt());//49
console.log('a' > 'b');//false
a的ascii小于b的ascii
console.log(‘a’>’A’)//true
小写字母的ascii大于大写字母
console.log('abc' > 'b');//false
a的ascii<b的ascii
console.log('abc' > 'aad');//true
第一位都是a ascii相同 第二位 b > a
var a = { b: 42 }
var b = { b: 43 }
a<b // false a b都是复杂类型,转换成原始值都是 "[object Object]" 所以为false
a==b // false 这里很容易混淆,注意a b都是复杂类型,需要比较地址 明显不同
a>b //false 和 a<b同理
a<=b // true <= 属于比较运算,不是判等运算,则不需要判断地址,a b 都是"[object Object]" 满足
a>=b // true 同上
注! javaScipt中的 > < >= <= 和数学中的不太一样,需要区分!
运算符 + - * /
+ - * / 最好理解,只需要记 + 有特殊
1. + 运算符,如果两边都是有字符串,就是字符串拼接,如果没有字符串,就都转成数字运算
2. - * / 都转换成数字运算
3. 复杂类型需先转换成简单类型!
看一些例子!
console.log('' + 1)//'1' -> "" + "1" -> "1"
console.log('a' + 1);//'a1' -> "a"+"1" -> "a1"
console.log('1' + 1);//'11' -> "1"+"1" => "11"
console.log(NaN + 1);//NaN 运算符两边都不是字符串,转成数字为NaN
console.log(true + 1);//2 -> 1+1 = 2
console.log(false + 1);//1 -> 0+1 = a
console.log(null + 1);//1 -> 0 + 1 = 1
console.log(undefined + 1);//NaN 注意 undefined转成数字为NaN null转成数字才是0
console.log([] + 1)//"1" []的原始值为"" ""+1 -> ""+"1" = "1"
console.log([1, 2, 3] + 1);//'1,2,31' [1,2,3]的原始值为"1,2,3" 相加为"1,2,31"
console.log({} + 1);
// -> "[object Object]"+1 -> "[object Object]"+"1" -> '[object Object]1'
console.log({ a: 1 } + 1);//'[object Object]1' 同理
注意 - * / 一律转换为数字运算
console.log('' - 1)//-1 -> 0-1 = -1
console.log('a' - 1);//NaN NaN和任意运算都是NaN
console.log('1' - 1);//0 -> 1-1=0
console.log(NaN - 1);//NaN NaN和任意运算都是NaN
console.log(true - 1);//0 -> 1-1 = 0
console.log(false - 1);//-1 -> 0-1 = -1
console.log(null - 1);//-1 -> 0-1=-1
console.log(undefined - 1);//NaN -> NaN - 1 -> NaN和任意运算都是NaN
console.log([] - 1)//-1 -> ""-1 -> 0-1 = -1
console.log([1, 2, 3] - 1);//NaN -> "1,2,3"-1 = NaN
console.log({} - 1);//NaN "[object Object]"-1 = NaN
console.log({ a: 1 } - 1);//NaN "[object Object]"-1 = NaN
最后,附上一张图吧! 看完了上面的内容,你能否看懂下面的图呢?
务必,不要相信直觉!!!