JavaScript "==" 的奇妙冒险
JavaScript数据类型
最新的 ECMAScript 标准定义了 8 种数据类型:
- Boolean
- Null
- Undefined
- Number
- BigInt
- String
- Symbol
- Object (引用类型)
== 两边操作数类型不同,导致的类型转换现象
在这之前,先来看一组奇怪的东西:
JS在 ”==“ 情况下的比较规则
在两个操作数类型不相等的情况下
- 如果有一个操作数是布尔值,则在比较相等性之前先将其转化为数值: false是0,而true为1
- 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转化位数值
- 如果一个操作数是对象,则调用对象的toString() 方法,转为toString()方法的返回值,接下来再按1,2的规则来
0 == false ,"" == false 和 1 == ture
这些都是根据上面规则进行转化,将字符串或者true 转化为数值来比较 自然就是true
[] == false 和 {} == false
[] == false 所以为 true 是因为 空数组 toString() 方法出来的是一个空字符串,相当于 ”“ == false 再通过规则1,2,便可以化为 0==0
所以比较为 ture
{} == false 为 false 则是因为 空对象**toString()**方法 出来的字符串为 "[object Object]",所以比较出来为空 false
如下图
但是好像大部分对象在没有重构toString方法的时候,都是输出的 "[object Object]"对吧?
所以,更奇妙的事情来了
无论对象内容是什么,只要跟 "[object Object]" 做比较,都会是true
接着如果我们对toString 进行重构,将其改为返回 ”我是一个对象“,就会发现这时候与 "[object Object]"比较起来就不相等了
此时与”我是一个对象“进行比较才会相等
那如果我们返回false 呢?
可以发现,如果这时候进行与false进行双等判断,返回为true ,也就说此刻相当于false == false
那么,众所周知,数组也是一个对象,那么快乐的双等它又来了:
当我们给 arr1 赋值一个空数组后,这时候它与false 进行判断为 true
但是当我们改变该数组的 toString方法以后,这就是和上面对象的判断一致了,必须要转为字符串
诚然这里我们是去改变了arr1的属性,但是如果我们去改变 arr1 的原型也是一样的 如下图
当然,不建议去改原型,这会导致所有的数组的toString方法发生改变,造成污染,污染如下图所示
== 两边操作数类型相同,导致的现象
== 两边操作数类型如果一致的话,那便是调用了 ===,进行比较的。所以该种情况下,调用 == 和 === 效果其实都是一样的
那么先上来一个好玩的:
这又是什么奇怪的玩意?
[] == [] : false 的原因
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script>
var arr1 = [];
var arr2 = [];
</script>
</head>
<body></body>
</html>
我们可以简单的创建两个数组,分别为arr1和arr2,通过谷歌浏览器打开,使用 Heap snapshot(堆快照)功能来获取arr1和arr2的内存地址(基于VM的虚拟机内存地址)如下图所示
其中 arr1 的地址为 @35171 ,arr2地址为@35173
=== 在两边操作数都为对象的时候,便是去比较这两个对象的地址,[] == [] 实际上创建了两个数组,自然这两个的地址页不一致,比较结果为false
那如果我们创建一个中间变量 arrx = [] ,自然 此刻堆 arr1和arr2进行判断后,结果自然是true 了,因为他们时候的内存是相等的
[] == ![] : true 的原因
其实这应该是 属于 == 两边操作符不同的内容,为什么呢?
我们可以来看看 js 中 八大类型的中关于 !的功能:
从上图可以发现所有的空值中,作为对象空值的!非常特殊,它为false!
其实从上面的堆快照截图中不难发现,对象的空值并不是传统意义上的空值,而是一个引用类型指向空对象的一种值
例如 arr1=[] 的实际值是@35171 ,而@35171才是指向堆空间中空数组的地址值,在栈空间中,是没有这个空数组的,
而***!其实是对栈空间中值的判断*** ,无论的Null,false,"",还是 0,他们在栈空间中的实际二进制都是一长串的0,但是@35171是一个地址,这是一个值,它不是0,所以 ![]和!{},才会变成false
因此 [] == ![] 实际上就是 [] ==fasle.
再结合上述 JS在 ”==“ 情况下的比较规则,所以 [] == ![] 的值才会是 true