跳到主要内容

类型转换

2023年02月10日
柏拉文
越努力,越幸运

一、规则


Preview

二、分类


  • 隐式类型转换: 在执行过程中当js内部期望得到某种类型的值,而实际在那里的值是其他的类型,就会发生隐式类型转换。系统内部会自动调用我们前面说ToBoolean ( argument )ToNumber ( argument )ToString ( argument ),尝试转换成期望的数据类型。

  • 显示类型转换: 手动调用Boolean(value)Number(value)String(value)完成的类型转换。

三、原理


  • ToPrimitive ( input [ , PreferredType ] ): ToPrimitiveinput装换为基本数据类型,PreferredType要么不传,要么是numberstring

    • PreferredTypenumber:

      • 如果 input 本身就是原始类型,直接返回 input

      • 调用 input.valueOf(),如果结果是原始类型,则返回这个结果。

        • number.valueOf():

          console.log((1).valueOf(), typeof (1).valueOf()); // 1 number
        • string.valueOf():

          console.log("1".valueOf(), typeof "1".valueOf()); // 1 string
        • boolean.valueOf():

          console.log((true).valueOf(), typeof (true).valueOf()); // true boolean
        • object.valueOf():

          console.log({}.valueOf(), typeof {}.valueOf()); // {} object

          console.log({ a: 1 }.valueOf(), typeof { a: 1 }.valueOf()); // { a: 1 } object
        • array.valueOf():

          console.log([].valueOf(), typeof [].valueOf()); // [] object

          console.log([1, 2].valueOf(), typeof [1, 2].valueOf()); // [ 1, 2 ] object
        • function.valueOf():

          console.log(function () {}.valueOf(), typeof function () {}.valueOf()); // [Function (anonymous)] function
      • 调用 input.toString(),如果结果是原始类型,则返回这个结果。

        • number.toString():

          console.log((1).toString(), typeof (1).toString()); // 1 string
        • string.toString():

          console.log("1".toString(), typeof "1".toString()); // 1 string
        • boolean.toString():

          console.log((true).toString(), typeof (true).toString()); // true string
        • object.toString():

          console.log({}.toString(), typeof {}.toString()); // [object Object] string

          console.log({ a: 1 }.toString(), typeof { a: 1 }.toString()); // [object Object] string
        • array.toString():

          console.log([].toString(), typeof [].toString()); // "" string

          console.log([1, 2].toString(), typeof [1, 2].toString()); // "1,2" string
        • function.toString():

          console.log(function () {}.toString(), typeof function () {}.toString()); // "function () {}" string
      • 抛出 TypeError 异常。

    • PreferredTypestring:

      • 如果 input 本身就是原始类型,直接返回 input

      • 调用 input.toString(),如果结果是原始类型,则返回这个结果。

        • number.toString():

          console.log((1).toString(), typeof (1).toString()); // 1 string
        • string.toString():

          console.log("1".toString(), typeof "1".toString()); // 1 string
        • boolean.toString():

          console.log((true).toString(), typeof (true).toString()); // true string
        • object.toString():

          console.log({}.toString(), typeof {}.toString()); // [object Object] string

          console.log({ a: 1 }.toString(), typeof { a: 1 }.toString()); // [object Object] string
        • array.toString():

          console.log([].toString(), typeof [].toString()); // "" string

          console.log([1, 2].toString(), typeof [1, 2].toString()); // "1,2" string
        • function.toString():

          console.log(function () {}.toString(), typeof function () {}.toString()); // "function () {}" string
      • 调用 input.valueOf(),如果结果是原始类型,则返回这个结果。

        • number.valueOf():

          console.log((1).valueOf(), typeof (1).valueOf()); // 1 number
        • string.valueOf():

          console.log("1".valueOf(), typeof "1".valueOf()); // 1 string
        • boolean.valueOf():

          console.log((true).valueOf(), typeof (true).valueOf()); // true boolean
        • object.valueOf():

          console.log({}.valueOf(), typeof {}.valueOf()); // {} object

          console.log({ a: 1 }.valueOf(), typeof { a: 1 }.valueOf()); // { a: 1 } object
        • array.valueOf():

          console.log([].valueOf(), typeof [].valueOf()); // [] object

          console.log([1, 2].valueOf(), typeof [1, 2].valueOf()); // [ 1, 2 ] object
        • function.valueOf():

          console.log(function () {}.valueOf(), typeof function () {}.valueOf()); // [Function (anonymous)] function
      • 抛出 TypeError异常。

    • PreferredType不传入

      • 如果 input 是内置的Date类型,PreferredType 视为 String

      • 否则 PreferredType 视为 ``Number。

  • ToBoolean ( argument )

    Preview
  • ToNumber( argument )

    Preview
  • ToString( argument )

    Preview

四、问题


  • ({}) + 1 的结果? :

    • 思路: + 运算符规则如下

      加法运算符+)为两种不同的运算重载: 数字加法字符串连接。在求值时,它首先将两个操作数强制转换为基本类型。然后,检查两个操作数的类型:

      • 如果有一方是字符串,另一方则会被转换为字符串,并且它们连接起来。

      • 如果双方都是BigInt,则执行 BigInt 加法。如果一方是 BigInt 而另一方不是,会抛出 TypeError

      • 否则,双方都会被转换为数字,执行数字加法

    • 过程:

      • {}1都会调用ToPrimitive,1原始类型直接返回
      • {}内部调用DefaultNumber,使用valueOf方法,返回object
      • 在调用toString方法,返回[object, object]
      • 所以最后的结果就是[object, object]1
  • String({a: 1}) 的结果?:

    • 执行转换String({a: 1})
    • 执行内部的ToString({a: 1})
    • {a: 1}不是原始类型,执行ToPrimitive({a: 1}, hint string)
    • 调用toString方法,返回"[object, object]"
    • 执行ToString("[object, object]"),返回"[object, object]"
  • {} + [] 的结果?:

    • 思路: + 运算符规则如下

      加法运算符+)为两种不同的运算重载: 数字加法字符串连接。在求值时,它首先将两个操作数强制转换为基本类型。然后,检查两个操作数的类型:

      • 如果有一方是字符串,另一方则会被转换为字符串,并且它们连接起来。

      • 如果双方都是BigInt,则执行 BigInt 加法。如果一方是 BigInt 而另一方不是,会抛出 TypeError

      • 否则,双方都会被转换为数字,执行数字加法

    • 过程:

      • {} + [] ==> {}.ToPrimitive({},default) + [].ToPrimitive([],default), [].ToPrimitive([],default) 会尝试调用 valueOf()toString() 转化成基础类型。由+运算规则可知, 优先调用 valueOf()

      • {} + [] ===> {}.valueOf() + [].valueOf() , {}.valueOf() 结果为 {} , [].valueOf() 结果为 [] , 不为基础类型,继续调用 toString() 尝试转化

      • {} + [] ===> {}.toString() + [].toString() , {}.toString() 结果为 [object Object][].toString() 结果为 "", 所以结果为 [object Object]

    • [] + [] 的结果?

      • 思路: + 运算符规则如下

        加法运算符+)为两种不同的运算重载: 数字加法字符串连接。在求值时,它首先将两个操作数强制转换为基本类型。然后,检查两个操作数的类型:

        • 如果有一方是字符串,另一方则会被转换为字符串,并且它们连接起来。

        • 如果双方都是BigInt,则执行 BigInt 加法。如果一方是 BigInt 而另一方不是,会抛出 TypeError

        • 否则,双方都会被转换为数字,执行数字加法

      • 过程:

        • [] + [] ==> [].ToPrimitive([], default) + [].ToPrimitive([], default) , 其中 [].ToPrimitive([], default) 结果不为基础类型,优先调用 [].valueOf() 继续尝试转换基础类型

        • [] + [] ==> [].valueOf() + [].valueOf(), 其中 [].valueOf() 结果不为基础类型,继续调用 [].toString() 继续尝试转换为基础类型

        • [] + [] ==> [].toString() + [].toString() , 其中 [].toString() 结果为 "",所以结果为 ""

五、总结


  • {} 转换原始数据的过程:

    • {}转换为String: 类型转换为String的时候优先调用toString,如果结果不是原始数据类型,那么继续调用valueOf()

      • result = ToPrimitive({}, String), ToPrimitive 的过程

        • 对象类型 {} 不为原始类型, 优先尝试调用 toString()

        • 优先尝试调用 {}.toString() , {}.toString() 结果为 字符串类型 "[object,Object]", 为原始类型, 返回 "[object,Object]"

      • 得出result = "[object Object]"

    • {}转换为Number: 类型转换为Number的时候优先调用valueOf,如果结果不是原始数据类型,那么继续调用toString()

      • result = ToPrimitive({}, Number), ToPrimitive 的过程

        • 对象类型 {} 不为原始类型, 优先尝试调用 valueOf()

        • 优先尝试调用 {}.valueOf() , {}.valueOf() 结果为 对象类型 {}, 为对象类型, 继续尝试调用 {}.toString()

        • 继续尝试调用 {}.toString() , {}.toString() 字符串类型 "[object,Object]", 为原始类型, 返回 "[object,Object]"

      • 得出result = "[object Object]"

    • {}转换为Boolean: 同上执行过程

  • { a:1 } 转换原始数据的过程:

    • { a:1 }转换为String: 类型转换为String的时候优先调用toString,如果结果不是原始数据类型,那么继续调用valueOf()

    • result = ToPrimitive({ a:1 }, String), ToPrimitive 的过程

      • 对象类型 { a:1 } 不为原始类型, 优先尝试调用 toString()

      • 优先尝试调用 { a:1 }.toString() , { a:1 }.toString() 结果为 字符串类型 "[object,Object]", 为原始类型, 返回 "[object,Object]"

    • 得出result = "[object Object]"

    • { a:1 }转换为Number: 类型转换为Number的时候优先调用valueOf,如果结果不是原始数据类型,那么继续调用toString()

      • result = ToPrimitive({ a:1 }, Number), ToPrimitive 的过程

        • 对象类型 { a:1 } 不为原始类型, 优先尝试调用 valueOf()

        • 优先尝试调用 { a:1 }.valueOf() , { a:1 }.valueOf() 结果为 对象类型 { a:1 }, 为对象类型, 继续尝试调用 {}.toString()

        • 继续尝试调用 { a: 1 }.toString() , { a:1 }.toString() 字符串类型 "[object,Object]", 为原始类型, 返回 "[object,Object]"

      • 得出result = "[object Object]"

    • { a:1 }转换为Boolean: 同上执行过程

  • [] 转换原始数据的过程:

    • []转换为String: 类型转换为String的时候优先调用toString,如果结果不是原始数据类型,那么继续调用valueOf()

    • result = ToPrimitive([], String), ToPrimitive 的过程

      • 对象类型 [] 不为原始类型, 优先尝试调用 toString()

      • 优先尝试调用 [].toString() , [].toString() 结果为 字符串类型 "", 为原始类型, 返回 ""

    • 得出result = ""

    • []转换为Number: 类型转换为Number的时候优先调用valueOf,如果结果不是原始数据类型,那么继续调用toString()

      • result = ToPrimitive([], Number), ToPrimitive 的过程

        • 对象类型 [] 不为原始类型, 优先尝试调用 valueOf()

        • 优先尝试调用 [].valueOf() , [].valueOf() 结果为 对象类型 [], 为对象类型, 继续尝试调用 [].toString()

        • 继续尝试调用 [].toString() , [].toString() 字符串类型 "", 为原始类型, 返回 ""

      • 得出result = ""

    • []转换为Boolean: 同上执行过程

  • [ 1,2 ] 转换原始数据的过程:

    • [ 1,2 ]转换为String: 类型转换为String的时候优先调用toString,如果结果不是原始数据类型,那么继续调用valueOf()

    • result = ToPrimitive([ 1,2 ], String), ToPrimitive 的过程

      • 对象类型 [ 1,2 ] 不为原始类型, 优先尝试调用 toString()

      • 优先尝试调用 [ 1,2 ].toString() , [ 1,2 ].toString() 结果为 字符串类型 "1,2", 为原始类型, 返回 "1,2"

    • 得出result = "1,2"

    • [ 1,2 ]转换为Number: 类型转换为Number的时候优先调用valueOf,如果结果不是原始数据类型,那么继续调用toString()

      • result = ToPrimitive([ 1,2 ], Number), ToPrimitive 的过程

        • 对象类型 [ 1,2 ] 不为原始类型, 优先尝试调用 valueOf()

        • 优先尝试调用 [ 1,2 ].valueOf() , [ 1,2 ].valueOf() 结果为 对象类型 [ 1,2 ], 为对象类型, 继续尝试调用 [ 1,2 ].toString()

        • 继续尝试调用 [ 1,2 ].toString() , [ 1,2 ].toString() 字符串类型 "1,2", 为原始类型, 返回 "1,2"

      • 得出result = "1,2"

    • [ 1,2 ]转换为Boolean: 同上执行过程

六、方案


6.1 通过 + 隐式转换

+ 运算符并且有一个操作数是 string 类型,此时+代表的是拼接。除了加号运算符,其他运算符,只要其中一方数据,那么另一方就被转换为数字

加号运算符规则

  • 只要有一边字符串,就把另外一边也转换为字符串,然后拼接
  • 两遍都没有字符串,只有有一边为数字,就把另一边转换成数字,然后运算