帮你理理JS中的==
写在前面
我和这个问题也算是有段因缘了,在初学JS时,就遇到了这个很蛋疼的东西,也算是JS中一个很杂的知识点,每次想起时都让我头疼不已,后来我决定花点时间去研究它,然后我查了一下《JavaScript权威指南》和《JavaScript高级程序设计》和文档,然后画了一张大致的图来帮帮助自己理解,大概就是下面那个
后来我发现这个图画的可能只有我自己看的懂,加上后来我学了更多的东西,对他也有了更深的了解,于是打算再写个博客分析一波这个东西。虽然这个东西,我是从来不用的,=== 它不香吗。
先了解一下JS的变量类型
JS中的变量类型大致可以分为两种,基本类型( primitive values )和引用类型( reference values )
基本类型有: Undefined, Null, Boolean, String, Symbol(es6新增), Number
引用类型有:Object
如果你不知道Symbol是什么的话,可以看看这个:http://caibaojian.com/es6/
理解==的运行过程
== 的比较分三种情况,我们假设 == 左边的值是x,右边的是y,这三种情况分别是,x和y都是基本类型,x和y都不是基本类型,x和y一个是基本类型一个是引用类型,下面分情况讨论
x和y都是基本类型
因为Symbol比较特殊,所以这里我们先不讨论Symbol的情况,留到最后再单独-讨论。
首先第一步,==看看x和y中有没有undefined或者null,如果有的话,看看另一边是不是undefined或者null,如果另一边也是undefined或者null,返回true,否则返回false==
也就是说,undefined == null是成立的,而且undefined和null只会等于他们自己或者对方,其他情况下都不相等,要问为什么的话,这是JS的历史遗留问题了。
1 | // 当出现undefined或者null时,只有以下三种为真,其他都为假 |
好,如果现在x,y中没有undefined或者null了,x和y必然是Number,Boolean,String三者中的任意一种,这个时候,有一个非常重要的原则,那就是,==先判断两边类型是否相等,相等的话就直接比较,否则把两边不是Number类型的值转化成Number再进行比较==
Number类型之间比较的规则如下
- 如果任意一边是NaN,返回false
- 其他情况按数值大小比较,相同返回true,否则为false
1
2
3
4
50 == "" // true, 空字符串转化成数字是0
0 == false // true,布尔值转化成数字是0
0 == "1" // false, "1"转化成的数字是1
0 == true // false,true转化成的数字是0
有关xy都是原始值的情况先告一段落,为了方便读者理解,转化成 Number类型调用的Number()函数留到最后再讲
x和y都是引用类型
这个情况比较简单,==只要比较他们引用的是不是同一个对象就行了==只有x和y引用的是同一个对象时,才返回true
1 | let x,y; |
x和y中一个是基本类型,一个是引用类型
首先我们要知道,==基本类型和引用类型是不能直接比较的,也需要类型转化==。类型转化的规则如下。
- 如果对象上设置了Symbol.toPrimitive属性而且是个函数,直接使用这个函数的返回值作为基本类型,如果这个函数返回的不是基本类型,会直接报错
- 依次调用对象上的valueof和toString方法把对象转化成基本类型的值,,如果valueOf不返回基本类型就调用toString方法,如果toString方法不返回基本类型会直接报错
- 转化成基本类型后,再使用基本类型间的比较方法进行比较
1 | let u = {}; |
1 | let u = { |
1 | let u = { |
有关valueof和toString的一些默认值会在文末写出。
当xy任意一边是Symbol类型时
之所以把Symbol提出来讲,是因为这个类型比较特殊,既不像基本类型也不像引用类型,下面一起来看看吧
- 如果一边是symbol,另一边不是,返回false
- 如果x和y是通过Symbol.for传入相同的参数生成的,那么返回true
- x和y都是类似于 Symbol.toPrimitive 这样的知名符号,而且两者是同一个知名符号时,返回true
- x和y使用了同一个symbol对象时,返回true
1 | let sym = Symbol("string"); |
1 | let x,y,z; |
1 | let x = Symbol.toPrimitive; |
1 | let x = Symbol("abc") |
补充
Number()的转化规则
1 | /* |
一些对象调用valueOf和toString的结果
其实如果你没有覆盖valueOf和toString方法的话,他们调用的一定是原型上的valueOf和toString方法
普通对象
- valueOf:得到对象本身
- toString:”[object Object]”
数组
- valueOf:得到数组本身
- toString:和数组调用join(“,”)的结果一样
正则
- valueOf:得到正则对象本身
- toString:转化成正则的字符串( /123/ 转化成 “/123/“ )
函数
- valueOf:得到函数本身
- toString:得到函数的字符串