Skip to content

JS 的数据类型转换

JS 是一种动态类型语言, 变量的数据类型在运行时才确定,并且在运行时还是可变的,而不是像C系,JAVA那样,在写代码时(编译时)就确定类型了。

在进行数值运算/比较时,JS 会自动转换类型。当然,我们也可以手动转换类型。

强制(显式)类型转换

Number 函数

Number 函数可以进行强制类型转换,需注意以下这些转换规则:

js
// 1. *纯数字字符串*转换为对应的 数字
Number('324') // 324
Number('0000123') // 123


// 2. *非纯数字字符串*转换为 NaN
Number('324abc') // NaN
Number('0-123') // NaN


// 3. *空字符串以及带转义字符的空字符串*转换为 0
Number('') // 0
Number(' \n \t  \r ') // 0
Number('   123') // 123 (前导空格被忽略)


// 4. *true 1 , false 0*
Number(true) // 1
Number(false) // 0

// 5. undefined NaN, null 0
Number(undefined) // NaN
Number(null) // 0

parseIntparseFloatNumber 的区别

前两者都是自左向右逐字符解析,而 Number全串匹配

parseInt 还有第二个参数radix,可以进行进制转换

js

// parseInt
parseInt("123abc");       // 123 OK的
parseInt("10", 2);        // 2  按二进制解析
parseInt("abc123");       // NaN 
parseInt("  42  ");       // 42 (前导空格被忽略)

// parsefloat
parseFloat("3.14abcd");   // 3.14 ✅
parseFloat("abc123.4");   // NaN ❌

Number(obj)会发生什么

简单来说:除非obj是一个包含单个数值的数值,否则 Number(obj) 总是返回NaN

具体来说,再 Number(obj) 时, 依次会发生:

  1. 调用 obj.valueOf() , 如果返回值原始类型,直接对该值使用 Number 函数
  2. 如果返回的还是对象类型,那就调用 obj.toString() ,如果返回值原始类型,直接对该值使用 Number 函数,一般就是到这一步,最终返回值为 NaN
  3. 如果返回的还是对象类型,那会报错
js
// 测试
const obj = {'a' : '1'}
obj.toString = ()=>{
    return obj
}

console.log(Number(obj)) 
// Uncaught TypeError: Cannot convert object to primitive value

String 函数

String 非常强悍,可以把任意值转为字符串, 主要体现为以下:

js

String(123) // "123"
String('abc') // "abc"

// true, false, undefined, null 都变为字面量字符串
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"


// 数组比较特殊,数组的 toString方法会把所有元素用 ,连接起来作为字符串
String([1, 2, 3]) // "1,2,3"

// 对象变为 [object Object],同样是先调用 valueOf, 再调用 toString
String({}) // '[object Object]'

Boolean 函数

除了以下五个值的转换结果为false,其他的值全部为true,在运算符章节中详细描述过

  • undefined
  • null
  • 0(包含-0和+0)
  • NaN
  • ''(空字符串)

自动(隐式)类型转换

自动类型转换具有不确定性,正式应用时,最好显示转换一下

主要场景:

  • 不同数据类型运算

  • 条件判断时自动转bool

  • 对非数值类型使用 + -

  • 具体可参考章节运算符

包装对象/装箱机制

三种原始类型的值数值、字符串、布尔值在一定条件下,也会自动转为“包装对象”(wrapper),这也是一些教程里说的 JS 的装箱机制(boxing)。

包装对象实现了运行时一切皆对象,让原始类型的值也能调用原型上的方法。

js
var str = 'abc';
console.log(str.length) // 3
// 等同于
var strObj = new String(str)
// String {
//   0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"
// }
console.log(strObj.length)

调用结束后,包装对象实例会自动销毁。这意味着,下一次调用字符串的属性时,实际是调用一个新生成的对象,而不是上一次调用时生成的那个对象,所以取不到赋值在上一个对象的属性,除非把新属性放在原型上。

js
var s = 'Hello World';
s.x = 'x';
console.log(s.x) // undefined

s.__proto__.y = 'y'
console.log(s.y)