数据类型转换

预计阅读时间: 7 分钟

数据类型转换介绍

JavaScript是一种动态类型语言,变量没有类型限制,可以随时赋予任意值

1const a = y ? 1 : '2';

上面代码中a的值是数字还是字符串取决于y的值是真还是假,也就是说a在编译的时候并不知道具体的类型,只有在运行的时候才能得到y的值然后知道a的类型

虽然变量的数据类型是不确定的,但是各种运算符对数据类型是有要求的 如果运算符发现,运算子的类型与预期不符,就会自动转换类型 比如,减法运算符逾期左右两边的运算子都是数值,如果不是,就会自动将他们转为数值

1'4' - '3' // 1

上面代码中,虽然是两个字符串相减,但是依然得到了1,因为JavaScript自动将他们的类型做了转换

接下来我们就来聊一聊JavaScript中怎么进行数据类型的转换

强制转换(显示类型转换)

强制类型转换主要使用Number(),String(),Boolean()这3个函数,手动将各种类型的值分别转换为数字,字符串或者布尔值

Number()

使用Number函数可以将任意的值转化为数值

下面分成两种情况讨论,一种参数是原始类型的值,另一种是对象类型

原始类型

原始类型的转换规则如下

1// 数值: 转换后还是原来的值
2Number(123) // 123
3// 字符串: 如果可以被解析为数值,则转换为对应的值  如果不能被解析为数值的话就会返回NaN  如果是空字符串的话则会返回0
4Number('123') // 123
5Number('123asdf') // NaN
6Number('') // 0
7// 布尔值: true转换为1,false转换为0
8Number(true) // 1
9Number(false) // 0
10// undefined: 转换为NaN
11Number(undefined) // NaN
12// null: 转换为0
13Number(null) // 0

这里我们特别讨论一下字符串转数值

我们知道,字符串转数值除了Number之外还可以用parseInt或者parseFloat

咱们用过的都知道parseIntparseFloat这两个方法把字符串解析为数值的时候都不怎么介意字符串中出现除数字外的字符

1Number('123asdf') // NaN
2parseInt('123asdf') // 123
3parseFloat('123asdf') // 123
4Number('123.111') // 123.111
5parseInt('123.11a1aaaa') // 123
6parseFloat('123.11a1aaaa') // 123.11

我们从上面的5/6行也能看出他们俩只要遇到了非数值之后就不解析了,不管你后面有没有数值都不解析了,就好像一个不是舔狗的打工人一样,数值就是他的工钱,我帮你解析一个字符,你就要给我一份工钱,如果你这个字符不是数值,那就相当于你赖账了,那我就不给你继续上班了,这个灵魂比喻应该还是比较形象的,嘿嘿

当然,虽然人家相对Number来说大气很多,但是也有特殊情况,就比如下面这两种

1parseInt('a123') // NaN
2parseFloat('a123') // NaN

你一开头就给人家一个非数字,别人什么好处都没有呢,当然直接就不给你解析了,随便甩给你一个NaN就完事了,你说是吧

对象

转换对象我们可以先来看几个例子

1Number({a: 1}) // NaN
2Number([1, 2, 3]) // NaN
3Number([1]) // 1

欸,第一个我理解,他是对象没法转成数值,那第二个和第三个都是数组,为什么一个可以转换另一个不能转换呢?

好,现在我们就来探讨一下他为啥一下可以一下不可以吧

Number他去转换的时候,发现你给他的是个对象,那对象怎么转换成数值啊,没有办法把,这个时候他就会通过其他方法,把你的对象转为自己认识的原始类型

  1. 调用对象自身valueOf方法,如果返回原始类型的值,则直接对这个值使用Number函数
  2. 如果调用valueOf返回的还是一个对象的话,就调用对象自身toString方法,如果返回原始类型的值,则对该值调用Number方法
  3. 报错

这就是Number函数转换对象时的心路历程了,valueOf -> toString -> Error

我们来模仿一下这个心路历程吧

1function toNumber(val){
2  let value = val
3  if(typeof value === 'object') {
4    value = val.valueOf()
5  }
6  if(typeof value === 'object') {
7    value = val.toString()
8  }
9  if(typeof value === 'object'){
10    throw new Error()
11  }
12  return Number(value)
13}

这段代码差不多就是他的心路历程了,一次次的确认他是不是object,就像你的对象一次次确认你是不是爱他一样,如果每次都是不爱,那我直接爆炸,只要有一次你爱我了,我就给你想要的(这么说好像舔狗)

好了那我们现在来看看争议最大的数组吧

1[1, 2, 3].valueOf() // [1, 2, 3]
2[1, 2, 3].toString() // '1,2,3'
3Number('1,2,3') // NaN
4
5[1].valueOf() // [1]
6[1].toString() // '1'
7Number('1') // 1
8
9[].valueOf() // []
10[].toString() // ''
11Number('') // 0

是吧是吧,没有一点点争议对吧~

当然这个toStringvalueOf方法我们也可以自己重写掉,我这里拿对象来举个例子吧

1const obj = {
2  valueOf() {
3    return 123
4  }
5}
6Number(obj) // 123
7
8const obj2 = {
9  toString() {
10    return '123'
11  }
12}
13Number(obj2) // 123
14
15const obj3 = {
16  valueOf() {
17    return obj
18  },
19  toString() {
20    return '234'
21  }
22}
23Number(obj3) // 234
24
25const obj4 = {
26  toString() {
27    return obj
28  }
29}
30Number(obj4) // TypeError: Cannot convert object to primitive value

看到这大家会不会很好奇obj3为什么返回的是234而不是obj.valueOf返回的123呢?

其实大家前面仔细看的话就会发现,Number类型转的心路历程中关心的一直都是你传入的那个对象,跟你的返回值没啥关系

自动转换(隐式类型转换)

JavaScript遇到逾期和实际不符的地方就会进行数据类型的转换,我们先来看几个例子

1'5' - '2' // 3
2'5' * '2' // 10
3true - 1 // 0
4false - 1 // -1
5'1' - 1 // 0
6'5' * [] // 0
7false / 5 // 0
8'1abc' - 1 // NaN
9null + 1 // 1
10undefined + 1 // NaN

这些例子中运算符两侧的运算子都被转成了数值进行运算 从‘abc’ - 1这个例子里我们就可以看出它内部类型转换的时候调用的是Number方法

当然一元运算符也会把运算子转换成数值

1+'123' // 123
2-'123' // -123
3+'asd' // NaN
4-'asd' // NaN
5+true // 1
6-false // 0