背景
前些日子做需求的时候遇到了一个bug。
原因是接口获取到的page值是字符串"2",切下一页的时候我直接写page+1去请求,发现根本没变成第3页的正常数据。一排查竟然请求的是第21页的内容。
这个现象引发了我对JS中数据类型隐式转换的好奇。东一榔头西一棒槌的学了不少观点,在此对我备忘录里记的知识点做个整理。内容不少,但看完一定有收获。
显示转换的方式
转换成字符串
.toString() :可以将number,boolean,object转化为字符串,除了null,undefined。
(1).toString() //'1'
true.toString() //'true'
({}).toString() //'[object Object]'
null.toString() //Uncaught TypeError: Cannot read properties of null (reading 'toString')
undefined.toString() //Uncaught TypeError: Cannot read properties of undefined (reading 'toString')
String():功能比toString强大一些
String(undefined) //'undefined'
String(null) //'null'
String(obj) //"[object Object]"
+空字符串
1 + '' //‘1’
转换成布尔值
转换过程中会有5个非true的值,分别是:NaN, 0, null, undefined, ''。
比较反常识的是空数组[]和空对象{}都对应布尔值true。
Boolean()
Boolean([]); // true
Boolean({}); // true
!!
!![] //true
!!0 //false
!!NAN //Uncaught ReferenceError: NAN is not defined
转换成数值
Number():空数组得到0,空对象及其他非数字的组成得到NaN
Number([]) // 0
Number({}) // NaN
Number('3') //3
Number('3r') //NAN
parseInt() parseFloat()
parseInt('20',10) //20
一元数学运算符 (- * /):
因为 JS 并没有类型声明,所以任意两个变量或字面量,都可以做加减乘除。
1 - true // 0, 首先把 true 转换为数字 1, 然后执行 1 - 1
1 - null // 1, 首先把 null 转换为数字 0, 然后执行 1 - 0
1 * undefined // NaN, undefined 转换为数字是 NaN
2 * ['5'] // 10, ['5']首先会变成 '5', 然后再变成数字 5
+ 运算符:
之所以把+单独拎出来,是因为JS里+不仅是个数学运算符,还可以用来拼接字符串。
这时候有三个原则需要遵守:
- 当一侧为String类型,被识别为字符串拼接,并会优先将另一侧转换为字符串类型。
- 一侧为Number类型,另一侧为原始类型,则将原始类型转换为Number类型。
- 一侧为Number类型,另一侧为引用类型,将引用类型和Number类型转换成字符串后拼接。
以上 3 点,优先级从高到低,即 3+'abc' 会应用规则 1,而 3+true会应用规则2。
[] + 0; // 0
{} + 0; // NaN
123 + '123' // 123123 (规则1)
123 + null // 123 (规则2)
123 + true // 124 (规则2)
123 + {} // 123[object Object](规则3)
隐式转换的触发
逻辑判断符
使用逻辑判断符时例如:if、while、for、!都会转换成布尔值。
console.log([] ? true : false) // true
console.log({} ? true : false) // true
!1 // false
空数组[]和空对象{}都是object类型,会被转化为true。
做==比较
虽然我们可以使用 ===避免不必要的状况,不过==里面发生的转换还是很有意思的。
下面五个规则参考 JavaScript 隐式类型转换,一篇就够了
- 规则 1:
NaN和其他任何类型比较永远返回false(包括和他自己)。
NaN == NaN // false
- 规则 2:Boolean 和其他任何类型比较,Boolean 首先被转换为 Number 类型。
true == 1 // true
true == '2' // false, 先把 true 变成 1,而不是把 '2' 变成 true
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规则转换为原始类型。
ToPrimitive规则,是引用类型向原始类型转变的规则,它遵循先valueOf后toString的模式期望得到一个原始类型。
如果还是没法得到一个原始类型,就会抛出 TypeError。
'[object Object]' == {}
// true, 对象和字符串比较,对象通过 toString 得到一个基本类型值
'1,2,3' == [1, 2, 3]
// true, 同上 [1, 2, 3]通过 toString 得到一个基本类型值
根据以上规则做两个题
1. [] == ![]
第一步,![] 会变成 false
第二步,应用 规则2 ,题目变成: [] == 0
第三步,应用 规则5 ,[]的valueOf是'',题目变成: '' == 0
第四步,应用 规则3 ,Number('')是0, 题目变成:0 == 0
所以, 答案是 true !
2. [undefined] == false
第一步,应用 规则5 ,[undefined]通过toString变成 '',题目变成 '' == false
第二步,应用 规则2 ,题目变成 '' == 0
第三步,应用 规则3 ,题目变成 0 == 0
所以, 答案是 true !
但是!! if([undefined]) 又是个true!
彩蛋(待补充)
上面说过了,在做对象的类型转换的时候,如果死磕源代码,里面会涉及到ToPrimitive,valueOf,toString,那么这里面到底是个什么机制呢?
let obj={abc:'1'}
console.log(obj.valueOf()) //{abc: '1'}
console.log(typeof obj.valueOf()) //object
console.log(typeof obj.toString())//string
(待补充)