类型转换:[ ] == ![ ]为什么返回true
前言
在JavaScript的世界里,类型转换和比较操作是编程日常中不可或缺的一部分,它们直接影响着代码的行为和逻辑判断的准确性。本文旨在深入探讨==(宽松相等)与===(严格相等)运算符的核心差异,以及背后涉及的类型转换机制,特别是对象到原始值的转换过程,帮助开发者更好地驾驭这门动态语言的微妙之处。
==
和 ===
的区别
先看一个例子:
在这个例子中你会发现 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()
。
对象转成原始值(重点内容)
1.任何对象转为布尔值一定是 true
2.对象转字符串
先执行ToString()
再调用ToPrimitive()
3. 对象转数字
先执行ToNumber()
再调用ToPrimitive()
ToPrimitive( )得到原始类型
查看 官方文档会发现对象转为原始值会先调用JS内置的ToPrimitive( )得到原始类型,下面来看看ToPrimitive( )是怎么回事吧。
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()方法,返回字符串字面量
valueOf()方法
valueOf()方法也可以将对象转成原始类型,但仅限于包装类对象( 如new Boolean()、new String()、new Number() )
隐式类型转换
一元运算符 +
一元 + 运算符就是将对象转成数字类型。在v8引擎中 +x
相当于 Number(x)
,例如 +[] == Number([])
。
在官方文档里面是这样描述一元运算符的:官方文档
翻译过来就是一元 + 运算符将其操作数转换为 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]'
== 运算
这是官方文档给的说明:
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
结语
这就是今天带给大家的内容啦,希望可以给你带来帮助!
转载自:https://juejin.cn/post/7371318721904443418