偷偷运行的逻辑 - JavaScript隐式类型转换
将写作当成兴趣,并一直进行下去。曾经这是个小小的奢望,现在已经在逐步的实现中。
长话短说,既然是技术文,就不发这么多感慨了,接下来,一起进入今天的正题吧。
今天给大家分享的是 JavaScript
中的隐式类型转换问题。相信很多的小伙伴都曾被它困扰过,不论是开发中还是面试过程中。期望今天的分享能给你带来不一样的理解。也能让你之后不再为此烦恼。
1. 基本数据类型
我们都知道,在 javascript
中一共有 7 中数据类型。分别是 Object, Null, Undefined, String, Number, Boolean, Symbol
。这些东西我们在平时的开发过程中每天无时无刻不在接触,这里我们不多赘述。
2. 强制类型转换
平时的使用过程中,我们会遇到很多数据类型不一致的问题,同样,强制将不同的数据类型转换为相同的数据类型也是很正常的操作;接下来,我们看看都有什么样的强制转换方法:
2.1 字符串 -> 数字
1. parseInt
parseInt('123') // 123
parseInt('123abc') // 123
parseInt('12.3abc') // 12
parseInt('abc123') // NaN
解释:
此方法只转换 以数字开头的,直到不是数字,然后转换结束,如果字符串不是以数字开头,则转换为 NaN
2. parseFloat
parseFloat('123') // 123
parseFloat('12.3abc') // 12.3
parseFloat('1.2.3abc') // 1.2
parseFloat('abc1.23') // NaN
解释:
规则同 parseInt
,只是注意小数点的转换,只能转换一个小数点,如果是多个小数点,则只保留一个。
3. Number
Number('123') // 123
Number('12.3abc') // NaN
Number('1.2.3abc') // NaN
Number('abc1.23') // NaN
解释:
此方法只转换 全数字 的字符串,如果字符串不是全数字。如'12.3abc','1.2.3abc','abc1.23'…………
的情况,统一转换为 NaN
4. 位运算
位运算的使用在前端是特别少的,以至于很多前端人员不清楚具体使用。下面我们来看看,如何使用位运算将字符串转换为数字
主要有以下几种操作方式可做转换
~
非<<
左移>>
右移
对于位运算的实际运算方式我们暂不做描述,本次只看如何使用它们将字符串转为数字
~~'123' // 123 (这里是两个 ~~ 波浪线)
'123' << 0 // 123
'123' >> 0 // 123
解释:
使用方法跟 Number
相同。都是只能转换全数字的字符串。不为全数字的字符串,~
转换为 NaN
,另外两种转换为 0
2.2 数字 -> 字符串
1. 使用 +
运算符。
'' + 123 // '123'
2. 使用 toString
方法
let num = 123
num.toString() // ‘123’
3. 使用 String
方法
let num = 123
String(num) // '123'
2.3 转换为布尔值
1. !! 方法。(双重否定即为肯定。使用双重非可以得到原始值转换的布尔值)
let num = 123
!!num // true
let str = '123'
!!str // true
2. Boolean
方法
let num = 123
let str = '123'
Boolean(123) // true
Boolean('123') // true
注意:
JavaScript
中为 false
的情况:
'',""
空字符串0
数字0
undefined
null
NaN
false
3. 隐式类型转换
3.1 可触发隐式类型转换的操作
- 四则运算
+, -, *, /
- 比较
> < >= <= ==
。- 注意:
===
是不会触发隐式类型转换的。
- 注意:
- 判断
if, while
3.2 toString
和 valueOf
说明
此两种方法是将复杂数据类型转换为原始值输出。
1. 调用 valueOf
方法后
String, Number, Boolean
返回的分别是 字符串值,数字值,布尔值。Object, Array, Function
返回的是自身Date
返回的是从开始到现在的毫秒值
2. 调用 toString
方法后
String, Number, Boolean
返回的本别是字符串类型的值Object
返回的是[object Object]
Array
返回的是 空字符串。因为在Array
中重写了这个方法function
返回的是函数本身的字符串Date
返回的是时间,并非毫秒数
注意:
在获取原始值(toPrimitive)时,会先调用 valueOf
方法,如果返回的不是原始值(也就是说返回的不是基本数据类型),则会继续调用 toString
方法。如果还不是原始值。则会报错。
3.3 具体实例解析
请在查看解析之前尝试解答下方问题
// 数组
[] == ![] // 1
[] == [] // 2
[] == false // 3
[] == true // 4
[1] == 1 // 5
[] == 0 // 6
[12] < [13] // 7
// 对象
{} == {} // 8
{} == !{} // 9
{} != {} // 10
// 结合版
[] + {} // 11
{} + [] // 12
{} + {} // 13
[] + [] // 14
{} + 1 // 15
1 + {} // 16
答案来咯,准备好了没
1. [] == ![]
执行步骤
// 将原题中的 ![] 转换为原始值 --> ![] 为false
[] == false
// 将 [] 转换为原始值 [].valueOf() 返回自身,继续调用 toString 返回 空字符串
'' == false
// 将空字符串转换为 布尔值,空字符串为false
false == false
// 得到结果为 [] == ![] --> true
2. [] == []
比较的是地址,两个数组的地址不相同。结果为false
3. [] == false
解答步骤同第一题[] == ![]
4. [] == true
解答步骤同第一题[] == ![]
5. [1] == 1
// 将 [1] 获取原始值, 调用 valueOf 返回自身,继续调用 toString 返回 '1'
1 == 1
// 得到结果 [1] == 1 --> true
6. [] == 0
// 将 [] 获取原始值, 调用 valueOf 返回自身,继续调用 toString 返回 ‘’
‘’ == 0
// 将空字符串转换为数字 '' --> 0
0 == 0
// 得到结果 [] == 0 --> true
7. [12] < [13]
// 将左右都转换为原始值
'12' < '13'
// 得到结果 true
8. {} == {}
同 [] == []
9. {} == !{}
同 [] == ![]
不同的是,这里的 {}
转化为字符串之后为[object Object]
。 所以结果与 [] == ![]
相反
10. {} !== {}
同 {} == {}
反结果
11. [] + {}
// 将左右同时获取原始值。
'' + '[object Object]' = '[object Object]'
12. {} + []
如果右边的值不值一个字典格式,则会将大括号当成一个空块儿处理。也就是说此时的表达式可以被转换成如下表达式+ []
,然后将 []
先转为空串,然后转换为数字,得到数字0
。
所以: {} + [] == 0
13. {} + {}
左右两边都做对象处理,获取原始值
'[object Object]' + '[object Object]' = '[object Object][object Object]'
14. [] + []
获取左右的原始值,都转换为了空字符串。然后做字符串拼接
'' + '' = ''
15. {} + 1
同 {} + []
,可变形为 + 1
16. 1 + {}
将右方的 {}
获取原始值,得到 '[object Object]'
,原式可变形为
1 + '[object Object]' = '1[object Object]'
4. 补充点
NaN
与任何值都不相等,包括它本身(这得多很,自己跟自己都不相等)- 有
undefined
参与的任意一个四则运算,结果都为NaN
- 布尔值
true
转数字时,转为1
;false
转为数字时为0
- 字符串之间比较大小,实际比较的是字符编码。如:
a > A = 97 > 65 = true
好了,今天的文章就分享到这儿咯,并没有写太多的概念,只是让大家来多看下实际运行的结果。一通则百通,知晓了实际运行的过程,再遇到相似的问题就游刃有余了。