数据类型的转换

335 阅读10分钟

数据类型的强制转换(显式转换)

强制转换主要是我们主动显式的通过String()、Boolean()、Number()函数转换数据类型,分别转化为对应的字符串、布尔值、数字。

Number()

使用Number函数,可以将任意类型值转化为数字。

这里分为两种情况讨论,一种是基本类型转化,另一种是复杂类型转化。

  1. 基本类型转化

    使用Number函数进行基本类型转化的时候,数字会转化为数字,纯数字字符串会转化为数字,如果含有无法转化为数字的部分,则会直接转化为NaN,空串('')会转为0,true会转化为1,false会转化为0,undefined会转化为NaN,null会转化为0。

    //1.纯数字转化
    Number(123) // 123
    //2.纯数字字符串转化(纯数字字符串会被转化为数字)
    Number('123') // 123
    //3.非纯数字字符串转化(非纯数字转化为NaN)
    Number('123a') // NaN
    //4.空串('')会转化为0
    Number('') // 0
    //5.布尔值true会被转化为1,false会被转化为0
    Number(true) // 1
    Number(false) // 0
    //需要注意的是,字符串的true和false会被认定为字符串,转化为NaN
    Number('true') // NaN
    Number('false') // NaN
    //6.undefined会被转化为NaN
    Number(undefined) // NaN
    //7.null会被转化为 0
    Number(null) //0
    

    需要注意的一点是,Number函数转化过程是很严格的,只要转化的内容中,有任何一个无法转化为数字就会返回NaN。相对来说parseInt的转化则是尽可能的将能转化为数字的转化,不能转化为数字的舍弃。

    Number('123number') // NaN
    parseInt('123number') // 123
    
  2. 复杂数据类型的转化

    相较于基本数据类型的转化,复杂数据类型转化就要复杂的多。不过大家只要记住,复杂类型的转化结果基本上为NaN,复杂的是其背后的隐式转化逻辑。只要不是单个数字的数组或空数组,其余全部情况都是NaN。

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

    为什么是这个结果,是因为Number的转换的背后规则非常复杂。转化主要有以下几个步骤:

    • 第一步,先调用目标对象的valueOf方法,如果返回的是基本数据类型,则直接进行Number函数转化,不再执行后续步骤。
    • 第二步,如果经过第一步的处理,返回的仍然是复杂数据类型,则会继续调用对象的toString方法,此时如果返回的是基本数据类型,则直接进行Number函数转化,不再执行后续步骤。
    • 第三步,如果第二步中的处理最后经过toString函数转化,得到的结果仍是对象,则会抛出错误。

    请看下面的转化示例:

    let arr = [1,2]
    Number(arr) // NaN// 首先,对原目标对象调用valueOf方法
    arr.valueOf() // [1,2]
    // 第二步,对返回的结果调用toString方法
    arr.valueOf().toString() // '1,2'
    // 最后,上述处理结果时字符串,通过Number函数转化
    Number('1,2') // NaN// 由此我们也知道了,为什么单数值数组为啥返回的是数字而不是NaN Number('1') => 1
    

    同样的,当我们将Object原型上的toString方法进行改写,让任意值的toString方法调用后,返回{},则会报错。

    //重写原型方法
    Object.prototype.toString = {}
    // 无法转化为基本数据类型,导致报错
    console.log(Number({})) //TypeError: Cannot convert object to primitive value
    

String()

String方法,则是将任意类型值转化为字符串。

同样的,也是分为转化基本数据类型和复杂数据类型。

  1. 基本数据类型的转化。

    使用String函数进行基本数据类型的转化,数值类型则会转化为对应的字符串;字符串转化还是原来的字符串;布尔类型则是转化为对应字符串,也就是true转化为"true",false转化为"false";未定义值undefined则会转化为"undefined";空值null则转化为"null"。

    //1.数值转化
    console.log(String(123));//123
    console.log(typeof String(123) === 'string') // true
    //2.字符串转化
    console.log(String('test'));//test
    console.log(typeof String('test') === 'string') // true
    //3.布尔值转化
    console.log(String(true));//true
    console.log(typeof String(true) === 'string') // true
    console.log(String(false));//false
    console.log(typeof String(false) === 'string') // true
    //4.未定义值undefined转化
    console.log(String(undefined));//undefined
    console.log(typeof String(undefined) === 'string') // true
    //5.空值null转化
    console.log(String(null));//null
    console.log(typeof String(null) === 'string') // true
    

    由此可见,经过String函数转化后的值均为string类型。

  2. 复杂数据类型转化

    String函数转化复杂数据类型如果是对象则会转化为[object Object]的字符串,函数则会转化为对应的字符串函数体,数组则会转化为对应数组的字符串形式。

    function test() {
        console.log('1');
    }
    console.log(String({ a: 1 })); //[object Object]
    console.log(typeof String(test)); //function test() {console.log('1');}
    console.log(String([1, 2, 3]));//1,2,3
    

    同样的,String函数转化为字符串的背后与Number函数转化过程类似,只不过第一步和第二步相较于Number函数的过程互换了一下,具体步骤如下:

    • 第一步,先调用目标对象的toString方法,如果返回的是基本数据类型,则直接进行String函数转化,不再执行后续步骤。
    • 第二步,如果经过第一步的处理,返回的仍然是复杂数据类型,则会继续调用对象的valueOf方法,此时如果返回的是基本数据类型,则直接进行String函数转化,不再执行后续步骤。
    • 第三步,如果第二步中的处理最后经过toString函数转化,得到的结果仍是对象,则会抛出错误。
    //1.正常的通过String函数转化对象
    console.log(String({ a: 1 }));//[object Object]
    //2.拆解String函数的转化过程
    console.log({ a: 1 }.valueOf());//{a:1}
    console.log({ a: 1 }.toString());//[object Object]
    //3.重写Object原型上的toString方法,使得toString方法转化后仍是复杂数据类型
    Object.prototype.toString = {}
    console.log(String({ a: 1 }));//报错:TypeError: Cannot convert object to primitive value
    

Boolean()

Boolean方法则是将任意类型值转化为布尔值。

相对于Number函数和String函数,Boolean函数的转化就简单直白了很多。

除了以下七个类型值会被转化为false外,其余的值均会被转化为true

这七个值分别是false/0n(ES2020引入的BigInt定义的0)/undefined/null/0(-0和+0)/NaN/''(空串)

console.log(Boolean(false));//false
console.log(Boolean(0n));//false
console.log(Boolean(undefined));//false
console.log(Boolean(null));//false
console.log(Boolean(0));//false
console.log(Boolean(-0));//false
console.log(Boolean(+0));//false
console.log(Boolean(NaN));//false
console.log(Boolean(''));//false

需要注意的是,Boolean函数转化布尔值则还是本身,空对象{}、空数组[]转化后则是true。甚至连false的包装对象new Boolean(false)都是true。

console.log(Boolean({}));//true
console.log(Boolean([]));//true
console.log(Boolean(new Boolean(false)));//true

因此,我们有以下结论:

所有其他值都会被转换为 true,包括对象、数组、字符串(非空)、数字(非零)、truefalse 的包装对象等。

因此,Boolean({})Boolean([])Boolean(new Boolean(false)) 都会返回 true,因为这些值都不是上述会被转换为 false 的值。

另一方面,!(逻辑非)运算符会将 true 转换为 false,将 false 转换为 true。因此,!Boolean({})!Boolean([])!Boolean(new Boolean(false)) 都会返回 false

这是因为在 JavaScript 中,对象、数组、字符串(非空)、数字(非零)等都被视为“真值”,而 false0-00n""(空字符串)、nullundefinedNaN 被视为“假值”。Boolean 函数和 ! 运算符用于将值转换为布尔值,以便在条件语句中使用。

自动转换(隐式转换)

下面介绍隐式转换,隐式转换本质上还是以强制转换的转换逻辑实现的,只是在使用的时候,转换过程不可见,是自动完成的。

主要有以下三种转换方式:

  • 第一种情况,不同类型的数据互相运算。

    123 + 'abc' //'123abc'
    
  • 第二种情况,在需要布尔值数据的地方使用非布尔值数据。

    //此处的if的判断条件见常见布尔值转化,除了那七个false,其他均为true
    if('a'){
        console.log('111')//'111'
    }else{
        console.log('222')
    }
    
  • 第三种情况,对非数值类型使用一元运算符(+或者-)

    + [1,2,3] //NaN
    + {a:1} //NaN
    !!{a:1} //true
    

    ! 实际上是转换布尔值的手段,有些时候开发中你会见到 !!xxx 的写法,实际上就是利用隐式转换,来将需要布尔值的地方,用这种隐式处理来实现。

自动转换实际上是js自动判断你需要什么样的类型,然后将对应的数据类型隐式转换为需要的类型。需要数字,就调用Number函数,需要字符串就调用String函数,需要布尔值就调用Boolean函数。

不过,我是并不推荐大家采用这种"取巧,简便"的方式来使用隐式转换。因为,隐式转换顾名思义就是"偷偷的,悄悄的"。可能你在开发的时候,一时用,一时爽,但是放在项目中是妥妥的定时炸弹。由于自动转换的不确定性,到了后面出现了bug,会导致错误的排查极其困难,因此在需要对应类型的地方,老老实实的使用强制转换,让其他开发者一目了然你在干什么。大大方方的使用Number函数、String函数、Boolean函数进行类型转换。

自动转换为Number类型

JS会在预期为Number类型的地方自动将目标值通过Number函数进行转化。背后的逻辑还是按照强制转换的处理方式,只不过这里是js帮我们自动转换。

只要是js预期为数值的地方,都会通过隐式转换进行处理。

console.log(1 + '2');//12
console.log(1 - '2');//-1
console.log(1 * '2');//2
console.log(1 / '2');//0.5
console.log(null + 1);//1
console.log(undefined + 1);//NaN
console.log(undefined + 1);//NaN
console.log(true + 1);//2
console.log(false + 1);//1

四则运算中,因为+ 会被js认为是拼接字符串,因此数字类型和字符串类型相加实际上是调用了String方法转化为了字符串。其余的都是数字的转换,这点需要注意。

另外,undefined会被转化为NaN,null会被转化为0。

自动转化为String类型

同样的,当js认为,预期可能是字符串的地方,就会隐式的调用String函数进行隐式转换。字符串的转换,主要发生在字符串拼接的加法运算中。当运算符的左右两边存在至少一个字符串类型数值后,运算的结果也都将是字符串类型。

console.log('1' + 0);// 10
console.log('1' + true);// 1true
console.log('1' + false);// 1false
console.log('1' + {});// 1[object Object]
console.log('1' + []);// 1
console.log('1' + function () { });// 1function () { }
console.log('1' + undefined);// 1undefined
console.log('1' + null);// 1null

由于是字符串数值的转换,因此此处运算的后半部分,实际上均通过String函数进行隐式转换,然后在进行字符串拼接。

前面数字转化的时候实际上也已经提示了,这样的运算很容易导致错误,比如你预期希望运算后的结果应该是一个Number类型,但是运算后的类型却是String类型。再次强调,不要因为开发时候偷点懒,导致后面维护的时候要你命三千!!!

let obj = {width:'100'}
obj.width + 10  //10010let obj = {width:'100'}
Numebr(obj.width) + 10  //20

自动转化为布尔值

布尔值的转化相对于Number和String类型一样,记住会被转化为 false 的情况就可以了。转化的场景也非常有限,比如一些if语句的判断条件,一些接收值非 true 就 false 的情况。老规矩,Boolean类型,展现 false 列表。

  • undefined
  • null
  • 0(+0/-0)
  • NaN
  • ''(空串)
  • 0n
  • false

除了上述值,其余的所有值只要是Boolean类型的转换,皆为true。通过排除上述的false值,得到的就是true。

if (!undefined && !null && !0 && !NaN && !'' && !0n && !false) {
    console.log(true);
}

以上就是数据类型转换的全部内容了,如有问题欢迎指出。