JS 数据类型 - 类型转换

124 阅读5分钟

显式类型转换

显式类型转换是指通过调用特定的函数或者方法来进行类型转换。

  • 转换为字符串
    • ​String()​ 函数:可以把任意类型的值转换为字符串,不管是基本数据类型还是对象类型。
    • ​toString()​ 方法:大部分对象都有 toString() 方法,能将对象转换为字符串。不过,null 和 undefined 没有 toString() 方法,调用会报错。
  • 转换为数字
    • ​Number()​ 函数:可把字符串、布尔值等类型转换为数字。如果转换失败,会返回 NaN。
    • ​parseInt()​ 函数:用于将字符串转换为整数,它会从字符串的开头开始解析,直到遇到非数字字符为止。
    • ​parseFloat()​ 函数:用于将字符串转换为浮点数,同样从字符串开头解析,能处理小数点。
  • 转换为布尔值 
  • ​Boolean()​ 函数:可将任意类型的值转换为布尔值。在 JavaScript 中,false​、0​、''​、null​、undefined​、NaN​ 会被转换为 false​,其余值转换为 true​ 。

隐式类型转换

隐式类型转换是指在某些运算或者操作时,JavaScript 自动进行的类型转换。

算术运算

  • 加法运算(+​)

    • 若两个操作数都是数值:执行常规的数学加法。

    • 若有一个操作数是字符串:另一个操作数会被转换为字符串,然后进行字符串拼接。

    • 对于对象、数组、布尔值等其他类型:通常会先尝试转换为原始值,若无法转换为数值则转换为字符串。

      // 数值与数值相加 - 略 // 字符串与字符串相加 - 略 // 字符串与其他类型相加 - 略

      // 对象、数组与其他类型相加: // 对象/数组 先调用 valueOf() 方法,如果返回的不是原始值,再调用 toString() 方法。 // 对象与数值 let obj = { valueOf: function() { return 20 } } console.log(obj + 5) // 输出 25 // 对象与字符串 let obj2 = { toString: function() { return "Object" } } console.log("This is an " + obj2) // 输出 "This is an Object" // 数组与字符串 let arr = [1, 2, 3] console.log("Array: " + arr) // 输出 "Array: 1,2,3"

  • 减法、乘法、除法和取模运算(-​、*​、/​、%​)

    这些运算符通常期望操作数是数值类型。如果操作数不是数值,JavaScript 会尝试将其转换为数值类型,转换规则(Number()​ 函数转换规则​)如下:

    • 字符串:如果字符串只包含有效的数字字符,则转换为对应的数值;如果字符串为空,转换为 0​;如果字符串包含非数字字符,转换为 NaN​。
    • 布尔值:true​ 转换为 1​,false​ 转换为 0​。
    • ​null​:转换为 0​。
    • ​undefined​:转换为 NaN​。
    • 对象和数组:先调用 valueOf()​ 方法,如果返回的不是原始值,再调用 toString()​ 方法,然后将结果转换为数值。

比较运算

  • 相等运算符(==​)和不相等运算符(!=​)

    ​==​ 和 !=​ 在进行比较时会进行隐式类型转换,将操作数转换为相同类型后再进行比较。

    • ​null​ 和 undefined​:null​ 只与 undefined​ 相等,即 null == undefined​ 为 true​,与其他值比较都为 false​。
    • 布尔值与其他类型比较:布尔值会先转换为数值,true​ 转换为 1​,false​ 转换为 0​,然后再进行比较。
    • 字符串与数值比较:字符串会尝试转换为数值,若转换成功则进行数值比较;若转换失败则结果为 false​。
    • 对象与原始值比较:对象会先调用 valueOf()​ 方法,如果返回的不是原始值,再调用 toString()​ 方法,然后将结果与另一个操作数进行比较。
  • 全等运算符(===​)和不全等运算符(!==​)

    ​===​ 和 !==​ 不会进行隐式类型转换,只有当两个操作数的类型和值都相等时,===​ 才返回 true​;反之,!==​ 返回 true​。

  • 关系运算符(<​、>​、<=​、>=​)

    关系运算符在比较时也会进行隐式类型转换。

    • 两个操作数都是字符串:按字典序比较。
    • 至少有一个操作数是数值:将另一个操作数转换为数值后进行比较。
    • 对象参与比较:先将对象转换为原始值,再按照上述规则进行比较。

逻辑运算

  • 逻辑非(!​)

    逻辑非运算符会把操作数转换为布尔值,然后取反。

    • ​undefined​、null​、0​、NaN​、空字符串 ''​ 会被转换为 false​,取反后为 true​。
    • 其他值会被转换为 true​,取反后为 false​。
  • 逻辑与(&&​)

    逻辑与运算符会从左到右依次计算操作数,并且会把操作数隐式转换为布尔值来判断真假。

    • 若第一个操作数转换为布尔值后为 false​,则返回第一个操作数,不再计算第二个操作数。
    • 若第一个操作数转换为布尔值后为 true​,则返回第二个操作数。
  • 逻辑或(||​)

    逻辑或运算符会从左到右依次计算操作数,并且会将操作数隐式转换为布尔值来判断真假。

    • 若第一个操作数转换为布尔值后为 true​,则返回第一个操作数,不再计算第二个操作数。
    • 若第一个操作数转换为布尔值后为 false​,则返回第二个操作数。

重点关注

Object​ 上 toString()​ 和 valueOf()​ 执行顺序与选择

  • 顺序:先 toSring()​ 后 valueOf()​
    // 直接输出对象名
    const obj = {
      valueOf: function() {
        console.log('调用 obj.valueOf')
        return '110'
      },
      toString: function() {
        console.log('调用 obj.toString')
        return {}
      },
    }
    alert(obj) // 【输出】调用 obj.toString  【输出】调用 obj.valueOf  【弹出】110
    
  • 顺序:先 valueOf()​ 后 toSring()​

    算术运算、比较运算以及某些布尔值转换的复杂隐式类型转换场景

  • 选择
    // 构造函数实例 
    // 算术运算 调用原型上的valueOf
    // 直接输出 调用原型上的toString
    class Test {
      valueOf () {
        console.log('调用 valueOf 方法')
        return 'v'
      }
      toString() {
        console.log('调用 toString 方法')
        return 's'
      }
    }
    const TT = new Test()
    // 调用示例
    TT + 1    // 输出:调用 valueOf 方法
    `${TT}`   // 输出:调用 toString 方法
    

经典案例

({}).valueOf()   // {}
({}).toString()  // "[object Object]"
([]).valueOf()   // []
([]).toString()  // ""
{} + {}          // "[object Object][object Object]"
{} + []          // 0  => 解析器把 {} 理解为代码块, {} 不参与加法运算,实际执行的是 + []
({} + [])        // "[object Object]"
({}) + []        // "[object Object]"
[] + {}          // "[object Object]"
[] + []          // ""
+ {}             // NaN
+ []             // 0
NaN !== NaN      // true
+undefined       // NaN