javascript中有8种比较运算符,分别是 > < <= >= == === != !==
这些比较运算符大致可分为三类:非相等运算、相等运算和不等运算
非相等运算
对于非相等比较来来说,如果两个运算元都是字符串,则比较每个字符的Unicode码点,不满足这个条件的话,就先将两个运算元先转换为数值,再继续比较
两个运算元都是字符串
两个运算元都是字符串,则分别对每个运算元的单个字符做 charCodeAt()运算,再相加,最终得出结果
对两个运算元的第一个字符做 charCodeAt 运算,如果比较出大小,则返回结果,否则依次比较第二个第三个字符。
'a' > `b` // => false'abc' > 'ba' // false'abc' > 'ab' // true'ab' > 'ba' // false
非字符串的比较
两个运算元至少有一个不是字符串需要分为两种情况
- 都是原始数据类型
- 存在引用数据类型(对象)
非字符串是原始数据类型
将其转换为数值再比较
5 > '4' // => true// 相当于 5 > Number('4')true > false // => true// 相当于 Number(true) > Number(false) 即 1 > 0null > undefined // => fales// Number(null)为0, Number(undefined)为NANundefined <= null // => false// Symbol 不能参与非相等比较,Symbol类型不能转换为数值// BigInt 和数值宽松相等,但不严格相等10n == 10 // true 10n就代表10的整数,10n === 10 // falseBigInt(10) === 10n // true
值得注意的是NaN和任何类型的值比较都是false
:::info 最新的ECMAScript标准定义了8种数据类型,其中七种原始数据类型,一种引用数据类型Object :::
存在引用数据类型
需要将其转换为原始数据类型,算法是先调用valueOf取值,如果得到的还是引用数据类型,再调用toString(),之后就是非字符串的原始数据类型比较
[2,3] > 3 // false// 相当于[2,3].valueOf().toString > 3 => "2,3" > 3 => NaN > 3 => falselet a = {}a > 4 // false// 相当于 a.valueOf().toStirng() > 4 => "[object object]" > 4 => NaN > 4 => falsea.valueOf = () => 5a > 4 // true
相等运算
javascript 中有三种相等运算
==宽松相等===严格相等Object.is()ES6中引入,为了解决===对于NaN===NaN为false的疑惑,以及+0===-0为true在元编程的不便
对于
==来说,当两个运算元的类型相同时,相当于调用===来比较,当两个元算元不同时,将他们转换为相同类型再调用===来比较, 这就是隐式类型转换
严格相等(===)
严格相等很简单,就是类型相等,而且值相等或者内存地址相同(对于引用类型来说)
:::warning
需要注意的是 NaN === NaN => false 和 +0 === -0 => true
:::
宽松相等(==)
当两个运算元类型相同时,就直接调用===来比较
类型不同时,需要通过隐式类型转换为相同类型再使用===比较。下面介绍隐式类型转换的算法
隐式类型转换大体可以分为三类
- 原始数据类型(除了null和undefined)
- null和undefined
- 一个引用数据类型和一个原始数据类型
Symbol 类型和任何值都不相等
Symbol('1') == '1' // falseSymbol('1') == Symbol('1') // false
由于本人对Symbol类型不太了解,拿来进行比较不知道是否恰当,如有不合理或错误的地方,请大佬指出
原始数据类型(除了null和undefined)
这种情况将两个运算元都转换为数值(Symbol类型除外),然后使用===比较
10 == '10' // true// 相当于 10 == Number('10')false == 0 // true// 相当于 Number(false) == 010n == '10' // true// 相当于 Number(10n) == Number('10') => true
null 和 undefined
null和undefined互相宽松相等且与其它所有类型的值都互相不宽松相等,但是存在一种特殊情况,在浏览器中,某些对象效仿undefined的角色,与null或undefeated宽松相等
null == undefined // truenull == 0 // falseundefined == 0 // falsenull == {} // false// document.all 在浏览器中充当undefined的角色null == document.all // trueundefined == document.all // true
一个引用数据类型和一个原始数据类型
算法:
- 如果原始数据类型是Boolean,将其转换为Number类型
- 引用数据类型的运算元调用valueOf方法,此时如果得到原始数据类型,则跳到步骤四(跳不跳无所谓)
- 调用toString方法,得到结果A
- 此时如果不是相同类型,则使用原始数据类型对应的包装器对象对A进行包装,得到相同类型
- 对两个运算元使用
===进行比较
false == [] // true// 相当于 Number(false) == [] => 0 == [] => 0 == Number([].valueOf().toString()) => truelet x = []x.valueOf = () => '10'x == 10 // truelet y = []y.valueOf = () => 'abc''abc' == y // true'0' == [] // false
:::info 当然,以上所说的原始数据类型不包括 null undefeated 和 Symbol :::
不等运算
!= 和 !== 这两个运算符的算法就是==或===的否定
:::warning
相等运算和不等运算满足交换律
但需要注意的是 {}作为第一个运算元时会报错Uncaught SyntaxError: Unexpected token '=='
:::
例如
[] == {} // false 相同类型,相当于 [] === {} => false{} == [] // 报错
最后,文中所说的“直接使用===来比较”并不准确,应该是使用 === 的算法更为恰当。本人能力有限,如有错误或不合适的地方,请大佬指正
