重学JavaScript之数据类型转换

142 阅读4分钟

在日常的业务开发中,经常会遇到JavaScript数据类型转换问题,有的时候需要我们主动进行强制转换,而有的时候js会进行隐式转换,隐式转换的时候就需要我们多加留心,具体会踩哪些坑呢。我们先来看一下代码:

'123' == 123 // false or true ?
'' == null // false or true ?
'' == 0 // false or true ?
[] == 0 // false or true ?
[] == '' // false or true ?
null == undefined // false or true ?
Number(null) //  ?
Number('') //  ?
parseInt('') //  ?
{} + 10 // ?

以上问题大家都不会陌生,基本涵盖了我们容易疏漏的一些情况。这就是在做数据类型转换时经常会遇到的强制类型转换和隐式转换的方式。

1.强制类型转换

强制类型转换包括

  • Number()
  • String()
  • Boolean()
  • toString()
  • parseInt()
  • parseFloat()

上述几个强制类型转换方法的原理大致相同,下面我挑两个比较代表性的方法来进行讲解:

Number()强制转换规则

输入输出
布尔值true转换为1,false转换为0
数字输出自身
null0
undefinedNaN
字符串1. 只包含数字,转换为十进制;2. 包含有效浮点格式,将其转化为浮点数值;3. 空字符串则即转换为0;4. 如果不是以上格式的字符串,返回NaN
Symbol抛出错误
对象先把object转为原始类型(下面会讲解),再适用以上规则

Boolean()强制转换规则

这个规则比较简单好记:除了undefined、null、false、''、0(包括+0,-0),NaN转换的结果都是false,其余都是true。

2.隐式类型转换

以下场景js会进行隐式类型转换:

  • 逻辑运算符&& 、||、 !
  • 运算符+、-、*、/
  • 关系操作符<、>、<=、>=
  • 相等运算符==
  • if/while条件

这里我选日常用的比较多的'+'和'==',详细讲解它们的隐式转换规则:

'=='的隐式转换规则

  1. 如果类型相同,无须转换
  2. 如果其中一个值是null或undefined,那么另外一个值必须为null或undefined才返回true,否则返回false
  3. 如果一个值是Symbol,返回false
  4. 如果两个操作值都为string和number,那么就会将为string的操作值转换为number
  5. 如果一个值是boolean,那么转换为number
  6. 如果一个值是object,且另一个值为string、number或Symbol,就会把object转为原始类型再进行判断(下面会讲解)

规则比较多,我们看看代码混个眼熟:

null == undefined // true, 采用规则2
null == 0 // false, 规则2
'' == 0 // true,  规则4
0 == false // true,  规则5

const obj = {
    value:0,
    valueOf: function() {
        this.value++;
        return this.value;
    }
}
// 经典面试题再现:obj可以既等于1,又等于2又等于3
console.log(obj == 1 && obj == 2 && obj == 3) // true, 规则6

'+'的隐式转换规则

加号操作符不仅可以用作数字相加,还可以用作字符串拼接。 当加号两边都是数字时进行的是加法运算,如果两边都是字符串,则直接进行拼接,无需进行隐式类型转换。 除了上述比较常规的情况外,还有一些特殊的规则:

  1. 如果一个值是string,一个是数字,则按照顺序拼接
  2. 如果一个值是string,一个是undefined、null或boolean类型,则调用其toString()方法进行字符串拼接。如果是对象,数组,正则等,则默认调用对象的转换方法会存在优先级,然后再进行拼接(下面会讲解)
  3. 如果一个值是number,一个是undefined、null或boolean或number类型,则将其转换为数字(见前面Number()强制转换规则)进行加法运算。对象情况参考上一条规则

看完规则看代码,方便理解:

'1' + undefined // '1undefined', 规则2
'1' + true // ‘1true’ , 规则2
'1' + 2 // '12', 规则1
1 + true // 2, 规则3
1 + undefined // NaN, 规则3
1 + 1n // 错误,不能将BigInt和number进行混合相加
'1' + 1n // '11' 比较特殊,字符串和BigInt相加,BigInt转换为字符串

object->基础类型的转换规则

前面有3个位置提到了object的转换规则,这里给大家做详细讲解,先上规则,注意有优先级:

  1. 如果该对象定义了【Symbol.toPrimitive】()方法,优先调用并返回
  2. 如果没有1,再调用valueOf()方法,如果转换为基础类型,则返回
  3. 如果没有1和2,再调用toString()方法,如果转换为基础类型,则返回
  4. 如果以上都没有返回基础类型,会报错

这就是比较偏底层原理的一些知识了,看看代码例子比较好理解:

const obj = {
    value: 1,
    valueOf() {
        return 2
    },
    toString() {
        return '3'
    },
    [Symbol.toPrimitive]() {
        return 4 // [Symbol.toPrimitive]()只能返回基础数据类型,不然报错
    }
}

obj + 1 // 5, 可以去掉Symbol.toPrimitive,那么会调用valueOf,返回3,如何再去掉valueOf,会返回'13'
10 + {} // '10[object Object]', 字面量对象{}会先调用valueOf()返回的还是{},继续调用toString()返回'[object Object]',再利用前面讲到的‘+’运算的规则,得到'10[object Object]'
[1,2,undefined,4,5] + 10 // '1,2,,4,5,10', 和上面例子一样

代码练习

如何让任意对象等于1?

Object.prototype.valueOf = function() {
    return 1
}
1 == {} // true
1 == {a:2} // true
1 == [] // true
1 == function() {} // true