写在前面
JavaScript 的动态弱类型特性使其在灵活性与复杂性之间形成了独特的数据类型转换机制。因此我们可以在声明变量时不用限制类型,并且可以随时赋予变量任意值。
js
let x = y ? 1 : '2'
如上变量 x 的类型需要在代码运行时才能确定,虽然变量类型不确定,但是运算符对变量的类型是有要求的,运算时若发现类型与预期不符,就会自动进行类型转换:
js
'2' - 1 // 1
上述代码中字符串 2 与数值 1 相减依然得到数值 1,原因就在于 JavaScript 将字符串自动转换为了数值。因此,理解 JavaScript 转换机制是避免代码陷阱、提升开发效率的关键。
数据类型的转换分为显式转换 和隐式转换
显式转换
显式主要指使用Number()
、String()
和Boolean()
三个函数,手动将各种类型的值,分别转换成数字、字符串或者布尔值。通过主动控制类型行为,降低了代码的隐式依赖风险。
Number() 转数值
使用 Number() 可以将任意类型转为数值,转换可以分为两种情况。
基本数据类型
基本数据类型转为数值规则如下:
js
// 1.数值:转换后还是原来的值
Number(123) // 123
// 2.字符串:可以被解析,转为对应的数值
Number('123') // 123
// 3.字符串:以 `0x` 开头是十六进制数字表示法转为对应的十进制数
Number('0x123') // 291
// 4.字符串:无法解析,返回 NaN
Number('123abc') // NaN
// 5.空字符串:转为 0
Number('') // 0
// 6.布尔值:true 转为 1,false 转为 0
Number(true) // 1
Number(false) // 0
// 7.undefined:转为 NaN
Number(undefined) // NaN
// 8. null 转为 0
Number(null) // 0
Number 实行的是严格转换,只要存在一个字符无法转为数值就会返回 NaN。
js
Number('123 abc') // NaN
parseInt('123 abc') // 123
Number 会去掉前置和后缀空格。
js
Number('\t\n123\v\r') // 123
parseInt('\t\n123.45\v\r') // 123
引用数据类型
简单说,其转换规则就是只要不是包含单个数值的数组,都转换为NaN。
js
Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([4]) // 4
真实转换过程就是 JavaScript 触发 ToPrimitive 的过程:
- 调用对象自身的 valueOf 方法,如果返回原始值,则直接对该值使用 Number(),否则进行后续步骤;
- 如果 valueOf 返回的不是原始值,则调用自身的 toString() 方法,如果返回原始值,则直接对该值使用 Number(),否则进行后续步骤;
- 如果 toString 返回的不是原始值则报错。
如下面列子所示:
js
let obj = {a: 1}
Number(obj) // NaN
// 等同于
const valueOf = obj.valueOf()
if (typeof valueOf === 'object') {
const toString = obj.toString()
if(typeof toString === 'object') throw new Error('Cannot convert object to primitive value')
Number(toString)
} else {
Number(valueOf)
}
默认情况下,对象的 valueOf 方法都是返回对象本身,所以一般总会调用 toString 方法,而对象的 toString 方法返回的是对象类型的字符串,如[object object] 。所以就有了
js
Number({a: 1}) // NaN
因此,当自定义了对象的 valueOf 或 toString 方法是就可以修改 Number 的返回值:
js
let obj1 = {
valueOf: () => 2
}
Number(obj1) // 2
let obj2 = {
toString: () => 3
}
Number(obj2) // 3
let obj3 = {
valueOf: () => 2,
toString: () => 3
}
Number(obj3) // 2
String() 转字符串
使用 String() 可以将任意类型转为字符串,转换也分为两种情况。
基本数据类型
基本数据类型转为字符串规则如下:
js
// 1.数值:转换为对应字符串
String(123) // '123'
// 2.字符串:转换后还是原来的值
String('abc') // 'abc'
// 3.布尔值:true 转为 'true', false 转为 'false'
String(true) // 'true'
String(false) // 'false'
// 4.undefined:转为 'undefined'
String('undefined') // 'undefined'
// 5.null:转为 'null'
String('null') // 'null'
引用数据类型
转换对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式。
js
String({a: 1}) // '[object object]'
String([1, 2, 3]) // '1,2,3'
String([1, 'a', 3]) // '1,a,3'
String([1, {}, 3]) // '1,[object object],3'
真实转换过程与 Number 大致相同,就是交换了调用 toString 和 valueOf 方法的顺序。
- 调用对象自身的 toString 方法,如果返回原始值,则直接对该值使用 String(),否则进行后续步骤;
- 如果 toString 返回的不是原始值,则调用自身的 valueOf 方法,如果返回原始值,则直接对该值使用 String(),否则进行后续步骤;
- 如果 valueOf 返回的不是原始值则报错。
js
let obj = {a: 1}
String(obj) // [object object]
// 等同于
const toString = obj.toString()
if (typeof toString === 'object') {
const valueOf = obj.valueOf()
if(typeof valueOf === 'object') throw new Error('Cannot convert object to primitive value')
Number(valueOf)
} else {
Number(toString)
}
因此也可以自定义对象的 toString 和 valueOf 方法修改 String 的返回值:
js
let obj1 = {
toString: () => '2'
}
String(obj1) // '2'
let obj2 = {
valueOf: () => '3'
}
Number(obj2) // [object object]
let obj3 = {
valueOf: () => '2',
toString: () => '3'
}
Number(obj3) // '3'
由于 toString 先于 valueOf 执行,所以 Number(obj2) 返回的还是 [object object]
Boolean() 转布尔值
使用 Boolean() 可以将任意类型转为布尔串。 以下值转换为 false,其余值转换都为 true:
- undefined
- null
- NaN
- 0 包含 +0、-0
- '' 空字符串,不包含空格、换行等
js
Boolean(undefined) // false
Boolean(null) // false
Boolean(NaN) // false
Boolean(0) // false
Boolean('') // false
Boolean(' ') // true
Boolean({}) // true
Boolean([]) // true
隐式转换
隐式转换是以显式转换为基础实现的,当遇到以下情况时 JavaScript 会自动进行数据类型转化。
1.转换为布尔值
当使用 if
、||
、&&
、!
或三元表达式等逻辑判断时,非布尔值会被隐式转换为布尔值。
js
if ( !undefined
&& !null
&& !NaN
&& !0
&& !''
) {
console.log('true');
} // true
let x = 1
!!x // true
x ? true : false // true
2.转换为字符串
使用 +
运算符时,若一侧为字符串,则另一侧转为字符串
js
'1' + 2 // '12'
'1' + true // '1true'
'1' + false // '1false'
'1' + {} // '1[object Object]'
'1' + [] // '1'1
'1' + function (){} // '1function (){}'
'1' + undefined // '1undefined'
'1' + null // '1null'
3.转换为数值
除了加法运算符 +
有可能把其他类型转为字符串以外,剩下的运算符都会把其他类型自动转成数值。
js
'2' - '1' // 1
'2' * '2' // 4
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'2' * [] // 0
false / '2' // 0
'a' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
另外使用一元运算符也可以将其他类型转成数值。
js
+'1' // 1
-'1' // -1
4.宽松相等 ==
- 若类型不同,会尝试将两者转为相同类型再比较;
- 转换优先级:数字 > 字符串 > 布尔值。
js
"5" == 5 // true(字符串转数字)
null == undefined // true(特例)
总结
JavaScript的类型转换机制是一把双刃剑:既提升了开发灵活性(如快速拼接字符串),也需警惕隐式规则带来的隐患(如0 == '0'
成立),避免"魔法代码"带来的困扰。以上就是 JS 数据类型转换的全部内容了,如果有什么问题欢迎大家补充。
第一次写文章,有什么可以改进地方求各位大佬指点。