跳到主要内容

类型转换

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 Number(value) 转换

6.2 parseInt(value) 转换

6.3 parseFloat(value) 转换

6.4 +value 加号运算符转换

规则: 除了+号运算符,其他运算符,只要其中一方数字,那么另一方就被转换为数字。+运算符规则如下:

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

结果: +号转换结果同Number, +(value) === Number(value)

6.5 ~~ value 位移运算符转换

结果: ~~ valueNumber(value) 结果类似,只不过将结果为NaN的转为0而已, 并将数字向下取整

  • 对于Number: 返回自己

    const value = 9;
    console.log(+value); // 9
    console.log(Math.floor(+value)); // 9
    console.log(~~value); // 9
    console.log(Number(value)); // 9
    console.log(Math.floor(Number(value))); // 9

    const value = 9.8;
    console.log(+value); // 9.8
    console.log(Math.floor(+value)); // 9
    console.log(~~value); // 9
    console.log(Number(value)); // 9.8
    console.log(Math.floor(Number(value))); // 9
  • 对于String: 如果字符串都为有效数字字符串,那么返回数字; 只要有一个不为数字字符,返回0

    const value = "32a";
    console.log(+value); // NaN
    console.log(Math.floor(+value)); // NaN
    console.log(~~ value); // 0
    console.log(Number(value)); // NaN
    console.log(Math.floor(Number(value))); // NaN

    const value = "32";
    console.log(+value); // 32
    console.log(Math.floor(+value)); // 32
    console.log(~~ value); // 32
    console.log(Number(value)); // 32
    console.log(Math.floor(Number(value))); // 32

    const value = " 32 ";
    console.log(+value); // 32
    console.log(Math.floor(+value)); // 32
    console.log(~~ value); // 32
    console.log(Number(value)); // 32
    console.log(Math.floor(Number(value))); // 32
  • 对于BigInt类型: 抛出 TypeError,以防止意外的强制隐式转换损失精度。

  • 对于Symbol类型: 抛出 TypeError

  • 对于Object类型: 返回0

    const value = {};
    console.log(+value); // NaN
    console.log(Math.floor(+value)); // NaN
    console.log(~~ value); // 0
    console.log(Number(value)); // NaN
    console.log(Math.floor(Number(value))); // NaN
  • 对于Array类型: 返回0

    const value = [];
    console.log(+value); // 0
    console.log(Math.floor(+value)); // 0
    console.log(~~ value); // 0
    console.log(Number(value)); // 0
    console.log(Math.floor(Number(value))); // 0

    const value = [1,2];
    console.log(+value); // NaN
    console.log(Math.floor(+value)); // NaN
    console.log(~~ value); // 0
    console.log(Number(value)); // NaN
    console.log(Math.floor(Number(value))); // NaN

    const value = [1];
    console.log(+value); // 1
    console.log(Math.floor(+value)); // 1
    console.log(~~ value); // 1
    console.log(Number(value)); // 1
    console.log(Math.floor(Number(value))); // 1

6.6 value >> 0value >>> 0 位移运算符转换

结果: value >> 0Number(value) 结果类似,只不过将结果为NaN的转为0而已, 并将数字向下取整

  • 对于Number: 返回自己

    const value = 9;

    console.log(+value); // 9
    console.log(Math.floor(+value)); //9
    console.log(value >> 0); // 9
    console.log(value >>> 0); // 9
    console.log(Number(value)); // 9
    console.log(Math.floor(Number(value))); //9

    const value = 3.6;

    console.log(+value); // 3.6
    console.log(Math.floor(+value)); //3
    console.log(value >> 0); // 3
    console.log(value >>> 0); // 3
    console.log(Number(value)); // 3.6
    console.log(Math.floor(Number(value))); //3
  • 对于String: 如果字符串都为有效数字字符串,那么返回数字; 只要有一个不为数字字符,返回0

    const value = "32a";

    console.log(+value); // NaN
    console.log(value >> 0); // 0
    console.log(value >>> 0); // 0
    console.log(Number(value)); // NaN

    const value = "32";

    console.log(+value); // 32
    console.log(value >> 0); // 32
    console.log(value >>> 0); // 32
    console.log(Number(value)); // 32

    const value = " 32 ";

    console.log(+value); // 32
    console.log(value >> 0); // 32
    console.log(value >>> 0); // 32
    console.log(Number(value)); // 32
  • 对于BigInt类型: 抛出 TypeError,以防止意外的强制隐式转换损失精度。

  • 对于Symbol类型: 抛出 TypeError

  • 对于Object类型: 返回0

    const value = {};

    console.log(+value); // NaN
    console.log(value >> 0); // 0
    console.log(value >>> 0); // 0
    console.log(Number(value)); // NaN
  • 对于Array类型: 返回0

    const value = [];

    console.log(+value); // 0
    console.log(value >> 0); // 0
    console.log(value >>> 0); // 0
    console.log(Number(value)); // 0

    const value = [1,2];

    console.log(+value); // NaN
    console.log(value >> 0); // 0
    console.log(value >>> 0); // 0
    console.log(Number(value)); // NaN

    const value = [8];

    console.log(+value); // 8
    console.log(value >> 0); // 8
    console.log(value >>> 0); // 8
    console.log(Number(value)); // 8

6.7 value >> 1value >>> 位移运算符转换

结果: 结果同>>value>>> valueNumber(value) 结果类似,只不过将结果为NaN的转为0而已, 并将数字除以2且向下取整

  • 对于Number: 返回自己

    const value = 9;

    console.log(+value); // 9
    console.log(Math.floor(+value / 2)); // 4
    console.log(value >> 1); // 4
    console.log(value >>> 1); // 4
    console.log(Number(value)); // 9
    console.log(Math.floor(Number(value) / 2)); // 4
  • 对于String: 如果字符串都为有效数字字符串,那么返回数字; 只要有一个不为数字字符,返回0

    const value = "32a";

    console.log(+value); // NaN
    console.log(Math.floor(+value / 2)); // NaN
    console.log(value >> 1); // 0
    console.log(value >>> 1); // 0
    console.log(Number(value)); // NaN
    console.log(Math.floor(Number(value) / 2)); // NaN

    const value = "32";

    console.log(+value); // 32
    console.log(Math.floor(+value / 2)); // 16
    console.log(value >> 1); // 16
    console.log(value >>> 1); // 16
    console.log(Number(value)); // 32
    console.log(Math.floor(Number(value) / 2)); // 16

    const value = " 32 ";

    console.log(+value); // 32
    console.log(Math.floor(+value / 2)); // 16
    console.log(value >> 1); // 16
    console.log(value >>> 1); // 16
    console.log(Number(value)); // 32
    console.log(Math.floor(Number(value) / 2)); // 16
  • 对于BigInt类型: 抛出 TypeError,以防止意外的强制隐式转换损失精度。

  • 对于Symbol类型: 抛出 TypeError

  • 对于Object类型: 返回0

    const value = {};

    console.log(+value); // NaN
    console.log(Math.floor(+value / 2)); // NaN
    console.log(value >> 1); // 0
    console.log(value >>> 1); // 0
    console.log(Number(value)); // NaN
    console.log(Math.floor(Number(value) / 2)); // NaN
  • 对于Array类型: 返回0

    const value = [];

    console.log(+value); // 0
    console.log(Math.floor(+value / 2)); // 0
    console.log(value >> 1); // 0
    console.log(value >>> 1); // 0
    console.log(Number(value)); // 0
    console.log(Math.floor(Number(value) / 2)); // 0

    const value = [1,2];

    console.log(+value); // NaN
    console.log(Math.floor(+value / 2)); // NaN
    console.log(value >> 1); // 0
    console.log(value >>> 1); // 0
    console.log(Number(value)); // NaN
    console.log(Math.floor(Number(value) / 2)); // NaN

    const value = [8];

    console.log(+value); // 8
    console.log(Math.floor(+value / 2)); // 4
    console.log(value >> 1); // 4
    console.log(value >>> 1); // 4
    console.log(Number(value)); // 8
    console.log(Math.floor(Number(value) / 2)); // 4