常见的3种数据类型转换
实际开发中,常用到的数据类型直接的的转换主要有如下3种:
- 把其他数据类型的值转换成 Number 类型
- 把其他类型的值转换成 String 类型
- 把其他类型的值转换成 Boolean 类型
把其他类型值转为 Number 类型
有 3 个函数可以将非数值转换为数值:Number()、parseInt()和 parseFloat()。
Number() 函数
实际开发中,一般不主动调用 Number() 函数,Number() 函数多用于隐式转换中。
以下情况浏览器会使用 Number() 来进行隐式转换:
- 进行数学运算时, 比如减法运算
10 - '5' - 使用 isNaN() 函数检测一个值是不是有效数字时
- == 比较时
+操作数只有一个,且在右边,例如:+ 'xxx', 一般都是把xxx转换为数字- ...
Number() 函数 转换规则如下:
- 把字符串转换为数字:空字符串变为0,如果出现任何非有效数字字符,结果都是NaN。
- 把布尔转换为数字:true->1 false->0
- null -> 0
- undefined -> NaN
- Symbol 无法转换为数字,会报错。
- BigInt 会去除“n”(超过安全数字的,会按照科学计数法处理)
- 把对象转换为数字,(浏览器会做以下几步操作):
- 先调用对象的 Symbol.toPrimitive 这个方法,如果不存在这个方法
- 再调用对象的 valueOf 获取原始值,如果获取的值不是原始值
- 再调用对象的 toString 把其变为字符串
- 最后再把字符串基于Number方法转换为数字
案例1
案例:把一个对象类型值转换为数字类型值
let obj = {
name: 'zhufeng'
};
console.log(Number(obj)); // NaN
转换过程描述:
- 浏览器会先看看 obj 是否有 Symbol.toPrimitive 属性。
obj[Symbol.toPrimitive];
// 输出结果: undefined
// 说明 obj 没有Symbol.toPrimitive 属性。
- 如对象没有Symbol.toPrimitive 属性,浏览器则调用当前对象的
valueOf()方法,获取其原始值。
obj.valueOf();
// 输出结果: {name:...}
// 是个对象,不是原始值。
- 如对象调用对象的
valueOf()方法获取到的不是原始值,则调用对象的 toString() 把 obj 变为字符串。
obj.toString()
// "[object Object]"`
- 最后再把得到的字符串,基于Number() 方法转换为数字。
Number("[object Object]");
// 输出结果:NaN
// 基于Number() 方法把字符串转数字,
// 只要字符串中有非有效数字,结果都是NaN
案例2
案例:把数组转换为数字类型值
let arr1 = [10],
arr2 = [10, 20];
console.log(Number(arr1)); // 10
console.log(Number(arr2)); // NaN
转换过程描述:
- 浏览器会先看看数组是否有 Symbol.toPrimitive 属性
arr1[Symbol.toPrimitive] // -> undefined 没有
arr2[Symbol.toPrimitive] // -> undefined 没有
- 如数组没有 Symbol.toPrimitive 属性,再调用数组的 valueOf() 方法,获取其原始值。
arr1.valueOf() // -> [10]
arr2.valueOf() // -> (2) [10, 20]
// 获取到的都不是原始值
- 调用数组的 toString() 把 数组 变为字符串
arr1.toString() // -> '10'
arr2.toString() // -> '10,20'
- 最后再把得到的字符串,基于Number() 方法转换为数字
Number('10') // -> 10
Number('10, 20') // -> NaN 字符串 '10,20' 中有非有效数字字符 “ ,”
案例3
日期对象转换为数字类型
let time = new Date();
console.log(Number(time)); // -> 623129321488
console.log( String(time) ); // -> "Tue Jun 08 2021 13:15:21 GMT+0800 (中国标准时间)"
转换过程描述:
- 先看看 time 是否有 Symbol.toPrimitive 属性
time[Symbol.toPrimitive]
// [Symbol.toPrimitive]() {[native code]}
// 有是个函数
- 如果对象有 Symbol.toPrimitiv 属性方法,则调用执行这个方法,执行完返回结果是什么,就是转换的结果就是什么。
执行这个方法时会传递一个 hint 值:'number'/'string' / 'default' , 传递的值不同,得到的结果不一样。
time[Symbol.toPrimitive]('number')
// -> 623129321488
time[Symbol.toPrimitive]('string')
// -> "Tue Jun 08 2021 13:15:21 GMT+0800 (中国标准时间)"
time[Symbol.toPrimitive]('default')
// -> Tue Jul 26 2021 18:29:50 GMT+0800 (中国标准时间)
案例4
给没有 Symbol.toPrimitive 属性的对象,我们可以给它扩展一个
// 1. 普通对象
let obj = {
name: 'zhufeng',
[Symbol.toPrimitive](hint) {
console.log(hint);
return 10;
}
};
// 浏览器会根据场景,传不同的 hint 值
console.log(Number(obj));
// hint->"number" Number(10) 10
console.log(String(obj));
// hint->"string" String(10) “10”
console.log(obj.toString()); //“[object Object]”
// 这不是把它转换为字符串「而是检测数据类型」,
// 所以不走 Symbol.toPrimitive 这一套逻辑
// 2. 数组
let arr = [10];
console.log(arr.toString());
//"10"
// 直接调用原型的 toString() 方法,
// 不会走 Symbol.toPrimitive 这套逻辑,方法内部做了处理
arr[Symbol.toPrimitive] = function (hint) {
console.log(hint);
return 0;
};
console.log(arr + "hhh");
// 0hhh
parseInt() & parseFloat()
语法:
parseInt([val],[radix])
parseFloat([val])
描述:
- 这两个函数主要用于将字符串转换为数值
- 一般用于手动转换
转换规则:
- parseInt() 第一个参数 [val],要被转换的值。
- 必须是一个字符串,
- 如果不是则先转换为字符串(强制隐式转换);
- 然后从字符串左侧第一个字符开始找,把找到的有效数字字符最后转换为数字
- 一个有效数字都没找到就返回
NaN - 如果遇到一个非有效数字字符,不论后面是否还有有效数字字符,都不再查找了,把找到的有效数字返回;
- 一个有效数字都没找到就返回
parseInt() 的二个参数 [radix], 可以指定进制。 + [radix] 是2-36之间的整数,如果不在这个范围内「排除0」,则结果一定是NaN + [radix]不设置或者设置为0,默认值是10「特殊:如果左侧字符串是以“0x”开始的,默认值是16」 + 从左侧[val]字符串中,查找出符合 [radix] 进制的字符,把找到的字符看做 [radix] 进制,最后转换为10进制。 + 其他进制转换为10进制:按权展开求和。 + 返回的结果,都是其他进制整数对应的10进制整数。
parseFloat() 规则同 parseInt() 一样,可以多识别一个小数点。parseFloat 没有第二个参数。
案例 1
案例 1: parseInt() 和 Number() 转换机制完全不一致
console.log(parseInt('10')); //10
console.log(Number('10')); //10
console.log(parseInt('10px')); //10
console.log(Number('10px')); //NaN
console.log(parseInt(null)); // parseInt('null') -> NaN
console.log(Number(null)); //0
案例 2
0开头数字会被识别为8进制数
console.log( parseInt(0023)) // 19
数值 0023 ,以0开头数字,会被浏览器认为是 8 进制数。
浏览器会将 0023 隐式转换成10进制整数展示。
0023 由8进制转成10进制,按权展开
08^3 + 08^2 + 28^1 + 38^0 -> 0 + 0 +16 + 3
结果为:19
案例 3
什么是按权展开求和?
// '1001' 以二进制身份转换为十进制
1*2^3 + 0*2^2 + 0*2^1 + 1*2^0 -> 8 + 0 + 0 + 1 -> 9
// '0.12' 以三进制身份转换为十进制
0*3^0 + 1*3^-1 + 2*3^-2 -> 0 + 0.3333... + 0.1111... -> 0.5555...
案例4
案例4:数组的 map 方法结合 parseInt() 应用
let arr = [27.2, 0, '0013', '14px', 123];
arr = arr.map(parseInt);
console.log(arr);
解析 题目把 parseInt 设定为 map 方法的回调函数,即 map 每遍历一次,就调用一次parseInt()。
由于 map 回调函数会被自动传入三个参数:数组元素,元素索引,原数组本身。
而parseInt(value [,radix]) 只有两个形参,所以只接收了数组元素,元素索引
所以在 parseInt调用时 ,当前数组元素 对应的是 value 是要被转化为整数的值,数组元素的索引对应的是 [,radix] 进制。
所以上题可以分解步骤为 :
-
parseInt(27.2,0) -> 27
- [radix] 进制:不设置或者设置为0,默认值是10
- parseInt(27.2,0) 即转换为10进制整数 -> 27
-
parseInt(0,1) -> NaN
- [radix] 进制:是2-36之间的整数,如果不在这个范围内「排除0」,则结果一定是NaN
- 1 不在范围内所以返回 NaN
-
parseInt('0013',2) -> 1
- [,radix] 进制 为 2, 有效数字值只有 0 和 1 ,
- 按规则从左往右,在字符串 '0013' 中查找有效数字,
- 找到的有效数字字符串是001',因为找到'3'时,已经不是2进制的有效数字值了,停止继续查。
- 再把 '001' ,按权展开,由2进制整数转换为10进制整数。
- -> 02^2 + 02^1 + 1*2^0
- -> 1
-
parseInt('14px',3) -> 1
- -> 同理,只有 '1' 符合三进制的
- -> 1*3^0 按权展开 ,由3进制整数转换为10进制整数
- -> 1
-
parseInt(123,4) -> 27
- '123' 符合四进制
- 14^2 + 24^1 + 3*4^0 -> 16 + 8 + 3 -> 27
- ---> 新数组:[27,NaN,1,1,27]
把其他类型值转为 Boolean 类型值
需要把其他类型值转为 Boolean 类型值的场景:
- 调用
Boolean() 转型函数 ! 逻辑非操作符也可以用于把任意值转换为布尔值。- 同时使用两个叹号(!!),相当于调用了
Boolean() 转型函数。 - 条件判断语句中,会(隐式)把值转换为布尔值。比如:if 语句的的条件,可以是任何表达式,但表达式的结果不一定是布尔值,JS引擎会(隐式强制)调用
Boolean() 转型函数。
转换规则:
- 只有6个值会被转换为false:
false,'',0,NaN,null,undefined; - 其他值都会被转成 true,包括空数组
[]和 空对象{}
把其他类型值转换为字符串
需要转换的场景:
- 调用
String() 转型函数 - 调用
toString() 方法 + 加法运算符,除了表示数学加法运算外,还可能是字符串拼接。
toString() 方法
toString() 方法,其作用就是返回当前值的字符串等价物
语法格式:
[value].toString();
哪些内置类的原型对象上有 toString() 方法
Array.prototype.toString()String.prototype.toString()Number.prototype.toString()Function.prototype.toString()Object.prototype.toString()【特殊】
在控制台依次输出以下代码:
(123).toString();
(true).toString();
('string').toString();
({}).toString();
(null).toString();
(undefined).toString();
输出结果如下图所示:
- 大部分都是拿引号将值包起来。
- 特殊 Object.prototype.toString
- null 或 undefined 没有toString() 方法,调用会报错。
参数问题: 多数情况下,toString()不接收任何参数。
不过,在对数值调用这个方法时,toString()可以接收一个[radix 进制]参数,即以什么[radix 进制] 来输出数值的字符串表示。默认情况下,toString()返回数值的十进制字符串表示。而通过传入参数,可以得到数值的二进制、八进制、十六进制,或者其他任何有效基数的字符串表示。
// 先把值转换成指定进制的数,再转成相应的字符串
let num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"
String() 函数
可以转任何数据类型的值,始终会返回表示相应类型值的字符串。
语法格式:
String(value)
转换规则:
- 如果值有 toString()方法,则调用该方法(不传参数)并返回结果。
- 如果值是 null,返回"null"
- 如果值是 undefined,返回"undefined"。
+ 加法运算符 会发生字符串拼接的场景
+ 两边都有操作数:
- 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面
- 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起
- 如果有任一操作数是对象、数值或布尔值,则先调用它们的 toString() 方法以获取字符串,再拼接字符串。
一些隐式转换规则总结
把对象隐式转换为数字/字符串的逻辑
- 当把对象隐式转换为数字或者字符串时,使用的方法是 Number() / String() ,会有一套处理逻辑:
- @1 检测Symbol.toPrimitive,如果有这个方法,浏览器会把方法执行,传递hint「根据场景不一样,浏览器默认传递的值也不同 'number'/'string'/'default'」;如果没有这个方法,则进行下一步;
- @2 基于valueOf获取原始值,如果获取的不是原始值,则进行下一步;
- @3 基于toString获取字符串
- @4 如果需要转的是数字,则再次把字符串转为数字即可
- 但是如果是直接
对象.toString,相当于直接调用第三个步骤「直接调用所属类原型上的toString方法」,此时直接获取字符串,不会走这四步逻辑。
“==”比较时候的相互转换规则
“==”相等,两边数据类型不同,需要先转为相同类型,然后再进行比较 @1 对象==字符串 对象转字符串「Symbol.toPrimitive -> valueOf -> toString」 @2 null==undefined -> true null/undefined和其他任何值都不相等 null===undefined -> false @3 对象==对象 比较的是堆内存地址,地址相同则相等 @4 NaN!==NaN @5 除了以上情况,只要两边类型不一致,剩下的都是转换为数字,然后再进行比较的 “===”绝对相等,如果两边类型不同,则直接是false,不会转换数据类型「推荐」