Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
本题难度:⭐ ⭐ ⭐
难度评级为个人主观,最高五星,最低一星。
答:JS 是一种动态类型语言,在声明变量时可以不必指定数据类型,而数据类型会在代码执行时会根据需要自动转换。
常见的类型转换有:
- 强制转换(显示转换)
- 自动转换(隐式转换)
显式转换
直接调用下面的 API 转换类型就是显式转换:
- Boolean()
- Number()
- parseInt() 和 parseFloat()
- String()
Boolean 类型
| 要转换的数据类型 | 转换为true的值 | 转换为false的值 |
|---|---|---|
| String | 非空字符串 | 空字符串 |
| Number | 非零数值(包括无穷值) | 0、NaN |
| Object | 任意对象 | null |
| Undefined | 无 | undefined |
Boolean('') // false
Boolean('lin') // true
Boolean('0') // true 这个要注意一下
Boolean(100) // true
Boolean(Infinity) // true
Boolean(0) // false
Boolean(NaN) // false
Boolean(undefined) // false
Boolean(null) // false
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
Boolean(function fn() { return false }) // true
Number 类型
| 要转换的数据类型 | 转换后的值 |
|---|---|
| String | 可以被解析为数值,则转换为相应的数值,不可以被解析为数值,返回 NaN |
| Boolean | true 为 1,false 为 0 |
| Undefined | NaN |
| Null | 0 |
| Object | 通常转换成 NaN(除了空数组或只包含单个数值的数组) |
Number('100') // 100
Number('001') // 1
Number('') // 0
Number('100qwe') // NaN
Number(false) // 0
Number(true) // 1
Number(undefined) // NaN
Number(null) // 0
Number({}) // NaN
Number({ age: 18 }) // NaN
Number([]) // 0
Number([1]) // 1
Number([1,2,3]) // NaN
Number(function fn() {}) // NaN
parseInt() 和 parseFloat()
Number转换的时候是很严格的,只要有一个字符无法转成数值,整个字符串就会被转为NaN
parseInt 相比 Number,就没那么严格了,parseInt 函数逐个解析字符,遇到不能转换的字符就停下来
特别注意,parseInt 解析空字符串为 NaN,Number 解析空字符串为0。
parseInt('100qwe') // 100
parseInt('qwe') // NaN
parseInt('') // NaN 这个要注意
parseFloat 工作方式和 parseInt 是一样的,只是 parseFloat 可以处理小数。
parseInt(5.433) // 5
parseFloat(5.433) // 5.433
parseFloat('5.43.3') // 5.43 parseFloat 只解析第一个小数点
String 类型
| 要转换的数据类型 | 转换后的值 |
|---|---|
| Number | 转为数值的字符串 |
| Boolean | true 转为字符串 'true',false 转为字符串'false' |
| Undefined | 'undefined' |
| Null | 'null' |
| Object | 比较复杂,见代码 |
String(0) // '0' 这里要注意,不是 ''
String(100) // '100'
String(001) // '1'
String(true) // 'true'
String(false) // 'false'
String(undefined) // 'undefined'
String(null) // 'null'
String({}) // '[object Object]'
String({ age: 18 }) // '[object Object]'
String([]) // ''
String([1]) // '1'
String([1,2,3]) // '1,2,3'
String(function fn() {}) // 'function fn() {}'
对象转原始类型
Symbol.toPrimitive是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。 -- mdn
对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:
- 如果已经是原始类型了,那就不需要转换了
- 如果需要转字符串类型就调用
x.toString(),转换为基础类型的话就返回转换的值。不是字符串类型的话就先调用valueOf,结果不是基础类型的话再调用toString - 调用
x.valueOf(),如果转换为基础类型,就返回转换的值 - 如果都没有返回原始类型,就会报错
相关知识,不懂戳这里 👇
知道这些知识之后,我们来分析一下前文的例子,
Number([1,2,3]) // NaN
[1,2,3].valueOf() // [1,2,3] 不是基础类型,去调 toString
[1,2,3].toString() // '1,2,3'
Number('1,2,3' ) // NaN,字符串类型转数字,不可以被解析为数值就返回 NaN
Number({}) // '[object Object]'
const obj = {}
{}.valueOf() // {},不是基础类型,去调 toString
obj.toString() // '[object Object]'
Number([object Object]) // NaN
还有很多其他例子,也可以用类似的思路去分析。
隐式转换
下面这些情况,JS 会发生隐式转换,
- 比较运算(
==、!=、>、<) - 算术运算(
+、-、*、/、%) - 逻辑语句
if、while需要布尔值的地方
当然,这些场景下运算符两边的操作数要不是同一类型,才会发生隐式转换。
算术运算符
1.减、乘、除
对各种非Number类型运用数学运算符(- * /)时,会先将非Number类型转换为Number类型。
100 - true // 99, 首先把 true 转换为数字 1, 然后执行 100 - 1
100 - null // 100, 首先把 null 转换为数字 0, 然后执行 100 - 0
1 * undefined // NaN, undefined 转换为数字是 NaN
2 * ['5'] // 10, ['5']首先会变成 '5', 然后再变成数字 5
2.加法
JS里 +还可以用来拼接字符串,所以要特殊一些,有下面3条规则:
- 当一侧为
String类型,被识别为字符串拼接,并会优先将另一侧转换为字符串类型。 - 当一侧为
Number类型,另一侧为原始类型,则将原始类型转换为Number类型。 - 当一侧为
Number类型,另一侧为引用类型,将引用类型和Number类型转换成字符串后拼接。
以上 3 点,优先级从高到低
123 + '123' // 123123 (规则1)
123 + null // 123 (规则2)
123 + true // 124 (规则2)
123 + {} // 123[object Object] (规则3)
逻辑运算符
if (1) {
// do something...
}
while (1) {
// 别这么写,会死循环的
// do something...
}
- undefined
- null
- false
- +0
- -0
- NaN
- ''
除了上面几种会被转化成
false,其他都换被转化成true
比较运算符
以 == 运算符举例
- 规则1:NaN 和其他任何类型比较永远返回 false(包括和他自己)。
NaN == NaN // false
- 规则2:Boolean 和其他任何类型比较,Boolean 首先被转换为 Number 类型。
true == 1 // true
true == '2' // false, 先把 true 变成 1,再参考规则3
true == ['1'] // true, 先把 true 变成 1, ['1']拆箱成 '1', 再参考规则3
true == ['2'] // false, 同上
undefined == false // false ,首先 false 变成 0,然后参考规则4
null == false // false,同上
- 规则3:String 和 Number 比较,先将 String 转换为 Number 类型。
123 == '123' // true, '123' 会先变成 123
'' == 0 // true, '' 会首先变成 0
- 规则4: null == undefined 比较结果是 true ,除此之外,null、undefined 和其他任何结果的比较值都为 false。
null == undefined // true
null == '' // false
null == 0 // false
null == false // false
undefined == '' // false
undefined == 0 // false
undefined == false // false
- 规则5:
原始类型和引用类型做比较时,引用类型会依照前文提过的ToPrimitive规则转换为原始类型。
'[object Object]' == {}
// true, 对象和字符串比较,对象通过 toString 得到一个基本类型值
'1,2,3' == [1, 2, 3]
// true, 同上 [1, 2, 3]通过 toString 得到一个基本类型值
- 规则6:两个都为引用类型,则比较它们是否指向同一个对象
let obj1 = { name: 'lin' }
let obj2 = { name: 'lin' }
obj1 == obj2 // false
let obj3 = { name: 'lin' }
let obj4 = obj3
obj3 == obj4 // true
其他比较运算符隐式类型转换规则和 == 是一样的,只是转换之后再做其他比较,比如 !=、>、<。
结尾
大汇总:
个人觉得把这张表搞懂,就能明白 JS 中的类型转换机制,至于其他很多稀奇古怪的东西,就别去研究了,以后全面拥抱 TS,这些问题都不是问题。
什么是稀奇古怪的东西?比如,
[] == ! [] // true
[] == [] // false
{} == !{} // false
{} == {} // false
又比如: 只用这 6 个字符,就可以写出任意 JavaScript 代码
JS 这门语言确实折磨人,还是 TS 好,没这么多奇奇怪怪的东西。
这是阿林持续更文的第 32 天,输出洞见技术,再会~
参考文章
上一篇:
上一篇: