类型转换:[ ] == ![ ]为什么返回true
前言
在JavaScript的世界里,类型转换和比较操作是编程日常中不可或缺的一部分,它们直接影响着代码的行为和逻辑判断的准确性。本文旨在深入探讨==(宽松相等)与===(严格相等)运算符的核心差异,以及背后涉及的类型转换机制,特别是对象到原始值的转换过程,帮助开发者更好地驾驭这门动态语言的微妙之处。
== 和 ===的区别
先看一个例子:
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/ba151cf2ca3746b39d69e643377c4124.webp)
在这个例子中你会发现 1 == '1' 会返回 true,而如果是 1 === '1' 会返回 false。那这是为什么呢?是因为当使用 == 进行比较时,JavaScript 会在比较之前尝试进行类型转换,使两个操作数具有相同的类型,然后再进行值的比较。而使用 === 运算符进行比较时,不仅比较值,还会比较类型,只有当两个操作数的值和类型完全相同时,结果才为 true。
今天要和大家聊一聊的就是使用 == 进行比较时,到底是怎么进行类型转换的。
在JS中不存在两个相等的对象
let a = [];
let b = [];
console.log(a == b);// 输出 false
在JS中不存在两个相等的对象,因为引用类型的大小是动态的,而栈内存是预先分配且大小固定的,不适合存储大小可变的数据结构。所以就将引用类型存储在堆中,并在栈中存放指向堆中数据的指针,这样保持了栈的轻量级,有利于提高程序执行效率,同时也保持了数据的隔离性,减少了潜在的数据冲突。
从上面的图可以看出[] == []在JS引擎的眼中等同于判断1001 == 1002,这无疑是false,所以可以得出一个结论:在JS中不存在两个相等的对象,因为每一个对象,也就是引用类型存放在堆里的指针都是不同的。
显示类型转换
1.原始值转布尔 Boolean(xx)
- 当原始值为数字时,只有
0和NaN是false,其他都是true。
console.log(Boolean(0));// 输出 false
console.log(Boolean(NaN));// 输出 false
console.log(Boolean(1));// 输出 true
console.log(Boolean(2));// 输出 true
console.log(Boolean(-1));// 输出 true
- 当原始值为字符串时,只有空字符串
''是false,其他都是true。
console.log(Boolean(''));// 输出 false
console.log(Boolean(' '));// 输出 true
console.log(Boolean('abc'));// 输出 true
- 当原始值为
undefined和null时都是false。
console.log(Boolean(undefined));// 输出 false
console.log(Boolean(null));// 输出 false
2.原始值转数字 Number(xx)
- 当原始值为字符串时,字符串里面的字符是数字,则输出里面的数字;字符串里面的字符是字母或者字,则输出
NaN(Not a Number);空字符串''输出为0。
console.log(Number('12'));// 输出 12
console.log(Number('589'));// 输出 589
console.log(Number('abc'));// 输出 NaN
console.log(Number('是昔年啊'));// 输出 NaN
console.log(Number(''));// 输出 0
console.log(Number(' '));//在里面加一个空格也是输出 0
- 当原始值为布尔值时,
false为0,true为1。
console.log(Number(false));// 输出 0
console.log(Number(true));// 输出 1
- 当原始值为
undefined,输出为NaN,当原始值null时,输出为0。
console.log(Number(undefined));// 输出 NaN
console.log(Number(null));// 输出 0
3.原始值转字符串 String(xx)
无论你传的是什么原始值,都会被转成字符串。
console.log(String(123));// 输出 '123'
console.log(String(NaN));// 输出 'NaN'
console.log(String(true));// 输出 'true'
console.log(String(undefined));// 输出 'undefined'
console.log(String(null));// 输出 'null'
console.log(String('abc'));// 输出 'abc'
4.原始值转对象 new xxx()
调用构造函数 new Number()、new String()、new Boolean()。
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/31de0568d62b4888adcc23dbb70442ec.webp)
对象转成原始值(重点内容)
1.任何对象转为布尔值一定是 true
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/2a2a4d329d8043a7822ae542853373bc.webp)
2.对象转字符串
先执行ToString()
再调用ToPrimitive()
3. 对象转数字
先执行ToNumber()
再调用ToPrimitive()
ToPrimitive( )得到原始类型
查看 官方文档会发现对象转为原始值会先调用JS内置的ToPrimitive( )得到原始类型,下面来看看ToPrimitive( )是怎么回事吧。
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/e909c55a9d3f4fe1be09ca67abc25f2b.webp)
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/3ee819814ba64afbbfc213f68bd769f8.webp)
JS内置的函数ToPrimitive(),可以把引用类型转换成原始类型,总结步骤如下:
ToPrimitive(obj,String) ==> 借助ToPrimitive()将对象转换成字符串
- 如果接收到的是原始值,直接返回值
- 否则,调用toString方法,如何得到原始值,返回
- 否则,调用valueOf方法,如何得到原始值,返回
- 报错
ToPrimitive(obj,Number) ==> 借助ToPrimitive()将对象转换成数字
- 如果接收到的是原始值,直接返回值
- 否则,调用valueOf方法,如何得到原始值,返回
- 否则,调用toString方法,如何得到原始值,返回
- 报错
toString()方法
{}.toString()得到由"[object 和 class 和 ]" 组成的字符串,即'[object Object]'[].toString()返回由数组内部元素以逗号拼接的字符串,即'',如果[1,2,3].toString(),即返回'1,2,3'- 其他的toString()方法,返回字符串字面量
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/0065e4a7d6f5472c9091a94a3399833f.webp)
valueOf()方法
valueOf()方法也可以将对象转成原始类型,但仅限于包装类对象( 如new Boolean()、new String()、new Number() )
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/61694620f7584af79d2a770966f61b61.webp)
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/c52d6d93154d4e4bb90216db7795b6ba.webp)
隐式类型转换
一元运算符 +
一元 + 运算符就是将对象转成数字类型。在v8引擎中 +x 相当于 Number(x),例如 +[] == Number([])。
在官方文档里面是这样描述一元运算符的:官方文档
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/c81fbefd192d400d9ed06bd3c685b599.webp)
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/788d728ab7fb46ffa52497ecce649df1.webp)
翻译过来就是一元 + 运算符将其操作数转换为 Number类型,会先执行ToNumber(),得不到数字的话再调用ToPrimitive()方法
console.log(+[]);// 输出为 0 步骤如下:
'+[]'
执行ToPrimitive([],Number)
[].valueOf()得[],不是原始类型,调用toString方法
[].toString()得到空字符''
'' 转数字得到 0
执行 +[] ,调用 ToPrimitive 方法,会先调用 valueOf 方法,但是 valueOf 方法无法将数组转换为原始值,就会开始调用 toString 方法。空数组调用 toString 方法会返回空字符串,然后JS会将空字符串转为数字 0 。
二元运算符 +
当执行表达式 v1 + v2 时,会有以下步骤:
- lprim = ToPrimitive(v1)
- rprim = ToPrimitive(v2)
- 如果 lprim 或者 rprim 是字符串,就会将不是字符串的那个转成字符串,那么就ToString(lprim) 或者 ToString(rprim) 再拼接
- 否则,就 ToNumber(lprim) + ToNumber(rprim)
console.log(1 + '1');// 输出 '11' 步骤如下:
1 + '1' ===> '11'
ToPrimitive(1) + ToPrimitive('1')
1 + '1'
ToString(1) + '1'
'1' + '1'最后得到 '11'
console.log(null + 1);// 输出 1 步骤如下:
ToPrimitive(null) + ToPrimitive(1)
null + 1
ToNumber(null) + ToNumber(1)
0 + 1
console.log([] + {});// 输出 '[object Object]' 步骤如下:
ToPrimitive([]) + ToPrimitive({})
[].valueOf() + {}.valueOf()
[].toString() + {}.toString()
'' + '[object Object]'
'[object Object]'
== 运算
这是官方文档给的说明:
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/6169b86b661b409789a83256a352cd7f.webp)
console.log(1 == {});// 输出 false 步骤如下:
1 == ToPrimitive({})
1 == {}.valueof() 判断不了
1 == {}.toString()
1 == '[object Object]'
1 == NaN
false
最后来到今天的重头戏,判断 [] == ![] 输出结果是什么?
console.log([] == ![]);// 输出 true 步骤如下:
[] == false
[] == 0
ToPrimitive([]) == 0
[].valueOf() == 0
[].toString() == 0
'' == 0
0 == 0
true
-
在JavaScript中,符号
!称为逻辑非运算符(Logical NOT Operator)。它是一个一元运算符,用于对一个布尔值进行取反操作。!优先级比==高,所以会先执行取反操作。空数组转为布尔值为true,所以![]的结果为false。 -
接下来相当于判断
[] == false,在官方文档中,如果有一个值为布尔值,先将其转换为数字,即将false转为了0,相当于要判断[] == 0。 -
当一边是对象,一边是数字时,比较结果
ToPrimitive([]) == 0,对于ToPrimitive([])会先调用 valueOf 方法,但使用这个方法并不返回原始值,接着调用 toString方法,返回结果为空字符串''。 -
最后相当于判断
'' == 0,将空字符串转为数字,结果为 0 ,判断0 == 0结果为true
结语
这就是今天带给大家的内容啦,希望可以给你带来帮助!
![类型转换:[ ] == ![ ]为什么返回true](https://static.blogweb.cn/article/78b6799c753144cf8111ad988f2e6cf7.webp)
转载自:https://juejin.cn/post/7371318721904443418