我们在写js代码的时候,有些需求要用到数据类型的转换,或者也可能是我们无意间的操作让数据发生了类型转换,从而引起错误。熟悉Js数据类型间的相互转换规则至关重要,让我们来看看这其中的猫腻吧。
本文将介绍把其他类型数据转换成“Number”、“String”、“Boolean”的规则,以及“==”操作比较时的相互转换规则。
Js中的数据类型转换有两种模式,一种是隐式转换,一种是显示转换。
隐式转换:浏览器默认去转换,用Number()
情景1:数学运算
console.log(10-'2');//8
//等价于
console.log(10-Number('2'));//8
情景2:isNaN检测
console.log(isNaN('2'));//false
//等价于
console.log(isNaN(Number('2')));//false
情景3:==比较
console.log('1'==1);//true
显示转换:自己手动用Number(),parseInt,parseFloat等
转换规则:
1.字符串转换为数字:空字符串变为0,如果出现任何非有效数字字符,结果都是NaN
console.log(Number(''));//0
console.log(Number('10'));//10
console.log(Number('10px'));//NaN
2.布尔值转换为数字:true–>1,false–>0
console.log(Number(true));//1
console.log(Number(false));//0
3.null–>0,undefined–>NaN
console.log(Number(null));//0
console.log(Number(undefined));//NaN
4.Symbol无法转换为数字,会报错:Uncaught TypeError: Cannot convert a Symbol value to a number
let a=Symbol('a')
console.log(Number(a));//Uncaught TypeError: Cannot convert a Symbol value to a number
5.BigInt去除"n"
console.log(Number(1n));//1
6.把对象转换为数字
(1)Date()对象转Number
let time = new Date();
console.log(time);//Thu Aug 18 2022 15:23:47 GMT+0800 (中国标准时间)
console.log(Number(time));//1660807427893
我们可以看到Number(time)输出的是毫秒数
在控制台中dir(time)
可以看到有原型上有Symbol.toPrimitive ,属性值是一个方法
按照规则,首先检测Symbol.toPrimitive,这里有,而且是个函数,就会执行!
我们可以试一下 time[Symbol.toPrimitive ](‘number’)
发现输出的也是毫秒数
(2)Number对象转Number
let num = new Number(10);
console.log(Number(num)); //10
按照规则,首先检测Symbol.toPrimitive,发现Number没有,所以num[Symbol.toPrimitive]—>undefined
然后会调用valueOf,而这里num.valueOf()—>10,是原始值,转换完毕
(3)[]转Number
let arr=[10]
console.log(Number(arr)); //10
按照规则,首先我们来看看arr有没有Symbol.toPrimitive
arr[Symbol.toPrimitive]—>undefined,表示没有这个值
其次调用arr.valueOf(),这个方法肯定有,任何对象都有这个方法
执行arr.valueOf()获取原始值—>[10],发现不是原始值,因为数组不是基本数据类型
然后调用toString
arr.toString()—>‘10’。最后再把字符串’10’转换成数字—>10,转换完毕
先看看与Number的区别:
console.log(Number('12px')); //NaN
console.log(Number(null)); //0
console.log(parseInt('12px') ); //12
console.log(parseInt(null)); //NaN
parseInt(val,radix)接受两个参数
进制 | 值 |
---|---|
2 | 0 1 |
3 | 0 1 2 |
8 | 0~7 |
10 | 0~9 |
16 | 0~9 A-F |
36 | 0~9 A-Z |
转换规则:从val字符串左侧第一个字符开始查找,查找出符合radix进制的值(遇到不符合的则结束查找,无论后面是否还有符合的),把找到的内容按照radix进制,转换为10进制!!!
举个例子:
console.log(parseInt('10102px13',2));//10
按2进制查找,找到1010,2不符合2进制,终止查找,查找结果是’1010’,再把这个二进制转换为10进制,最终结果就是10
再来看一道稍微复杂一点的题目:
let arr = [27.2, 0, '0013', '14px', 123]
arr = arr.map(parseInt)
console.log(arr); //[27,NaN,1,1,27]
解析:
这里的map相当于
arr.map((item,index)=>{
return parseInt(item,index)
})
展开就是下面这样
parseInt(27.2,0)
parseInt(0,1)
parseInt('0013',2)
parseInt('14px',3)
parseInt(123,4)
我们一个个分析:
parseInt(27.2,0):27.2按10进制找,找到小数点停止,得’27’用10进制转成10进制,最终结果是27
parseInt(0,1):1不在有效的进制范围,直接返回NaN
parseInt(‘0013’,2):0013按2进制找,得’001’,2进制001转成10进制,最终结果是1
parseInt(‘14px’,3):14px按3进制找,得’1’,3进制1转成10进制,最终结果是1
parseInt(123,4):123按4进制找,得’123’,4进制123转成10进制,最终结果是27
所以最终输出的是[27,NaN,1,1,27]
特殊情况:
console.log(parseInt('0013',2)); //1
console.log(parseInt(0013,2)); //3
原因:JS中以0开头的数字类型,会默认把其按照8进制转成10进制,然后再进行其他操作
所以parseInt(0013,2),首先把0013从8进制转成10进制,得11,然后再将11按2进制转成10进制,最终结果是3
parseFloat的转换规则比较简单,与parseInt的一样,只是没有进制的概念,都按10进制来
console.log(parseFloat(10.1)); //10.1
console.log(parseFloat('10.1')); //10.1
console.log(parseFloat(null)); //NaN
console.log(parseInt(10.1)); //10
转换规则:除对象以外,都是直接用字符串包起来
console.log(String(1)); //'1'
console.log(String(NaN)); //'NaN'
console.log(String(null)); //'null'
console.log(String(undefined)); //'undefined'
console.log(String(Symbol())); //'Symbol()'
console.log(String({})); //'[object Object]'
对象转字符串的转换规则,与对象转数字的规则一样:
除了数学运算,还可能代表字符串拼接,也会发生字符串转换
例1:
console.log(10+'10'); //'1010'
直接拼接
例2:
console.log(10+new Date()); //10Thu Aug 18 2022 17:25:52 GMT+0800 (中国标准时间)
按对象转字符串的转换规则
先new Date()[Symbol.toPrimitive] (‘default’)—>‘Wed Aug 03 2022 17:06:42 GMT+0800 (中国标准时间)’
得到字符串,然后拼接上10—>‘10Wed Aug 03 2022 17:06:42 GMT+0800 (中国标准时间)’
例3:
console.log(10+[10]);//'1010'
先[10] [Symbol.toPrimitive]—>undefined
然后[10].valueOf()—>[10]
再然后[10].toString()—>‘10’
最后10+'10’字符串拼接—>‘1010’
例4:一道面试题
let result=100+true+21.2+null+undefined+"ABC"+[]+null+9+false
console.log(result); //NaNABCnull9false
这道题就留给读者分析吧
要注意undefined在运算中会变成NaN,任何数加NaN都等于NaN
i是数字类型的时候
let i=10;
i++
console.log(i); //11
等价于
let i=10;
i=i+1;
console.log(i); //11
i是字符串类型的时候
连续++一定是数学运算!!!
let i='10';
i++
console.log(i); //11 连续++一定是数学运算
不等价于
let i = '10';
i = i+1;
console.log(i); //101 '10'+1->'101'
利用这个原理,我们有个编写Js代码的小技巧
加个+就能转成数字类型
let num='10'
console.log(+num); //10
console.log(typeof (+num));//number
数字加个空字符串也能转为字符串
let str=10
console.log(str+''); //'10'
console.log(typeof (str+''));//string
转换规则:
除了“0/NaN/空字符串/null/undefined"五个值是false,其余值都是true
情景:
这个比较简单就不举例了,记住就行
按对象转字符串规则Symbol.toPrimitive—>valueOf—>toString
将对象转成字符串,然后比较
console.log(null==undefined);//true
console.log(null===undefined);//false
注意,null或undefined和本身比较是相等的,和其他任何值比较都不相等
console.log(null==null);//true
console.log(null===null);//true
console.log(null=='null');//false
console.log(undefined==undefined);//true
console.log(undefined===undefined);//true
console.log(undefined=='undefined');//false
比较的是堆内存地址,地址相同则相等
console.log(NaN==NaN);//false
console.log(NaN===NaN);//false
NaN与任何值都不相等,包括NaN本身
"==="绝对相等,如果两边类型不同,则直接是false,不会发生数据类型转换。
例1:
console.log([]==false); //true
解析:都按前面提到的规则,转换为数字
第一步:[][Symbol.toPrimitive]—>undefined,没有Symbol.toPrimitive
第二步:[].valueOf()—>[],不是原始值
第三步:[].toString()—>‘’,得到空字符串,最后转换成0
第四步:按布尔值的转换规则,false—>0
第五步:0==0—>true
例2:
console.log(![]==false);//true
先处理![],进行布尔类型转换,[]—>true,所以![]—>false
最后false==false—>true