JavaScript 数据类型转换规则深度解析

写在前面

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​ 的过程:

  1. 调用对象自身的 valueOf 方法,如果返回原始值,则直接对该值使用 Number(),否则进行后续步骤;
  2. 如果 valueOf 返回的不是原始值,则调用自身的 toString() 方法,如果返回原始值,则直接对该值使用 Number(),否则进行后续步骤;
  3. 如果 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 方法的顺序。

  1. 调用对象自身的 toString 方法,如果返回原始值,则直接对该值使用 String(),否则进行后续步骤;
  2. 如果 toString 返回的不是原始值,则调用自身的 valueOf 方法,如果返回原始值,则直接对该值使用 String(),否则进行后续步骤;
  3. 如果 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 数据类型转换的全部内容了,如果有什么问题欢迎大家补充。

第一次写文章,有什么可以改进地方求各位大佬指点。

相关推荐
聪明的墨菲特i31 分钟前
React与Vue:哪个框架更适合入门?
开发语言·前端·javascript·vue.js·react.js
拉不动的猪39 分钟前
v2升级v3需要兼顾的几个方面
前端·javascript·面试
冴羽1 小时前
SvelteKit 最新中文文档教程(20)—— 最佳实践之性能
前端·javascript·svelte
Nuyoah.1 小时前
《Vue3学习手记2》
javascript·vue.js·学习
zpjing~.~1 小时前
css 二维码始终显示在按钮的正下方,并且根据不同的屏幕分辨率自动调整位置
前端·javascript·html
lynx_2 小时前
又一个跨端框架——万字长文解析 ReactLynx 实现原理
前端·javascript·前端框架
夜寒花碎2 小时前
前端基础理论——02
前端·javascript·html
uhakadotcom2 小时前
简单易懂的Storybook介绍:让前端UI组件开发变得更高效
前端·javascript·面试
bnnnnnnnn2 小时前
前端实现多服务器文件 自动同步宝塔定时任务 + 同步工具 + 企业微信告警(实战详解)
前端·javascript·后端
萧门竹巷3 小时前
你可能不知道的 HTML5 新特性——「鲷哥」真的好用!
javascript