Javascript数据类型之类型转换

 如果你只想知道不同情况下使用何种方式来进行类型转换,请直接看总结。如果你还想进一步的了解不同方式的特点,请直接看转换到具体类型每一节的小结。当然我更希望您能耐心看完全文。
  javascript数据类型可以相互转换,既可以调用相应的javascript函数来显式类型转换,javascript引擎也能够自动的进行隐式类型转换。我们来看一个示例:


    var age = 30;
    age.toString() + 'year old'; // '30 year old'
    age + ' years old'; // '30 years old'


  在运行age.toString() + 'year old'时,age变量通过调用toString函数显式的从Number型转换成String型。而在运行age + ' years old'时,我们没有显式的调用函数,age也被转换成String型,这里就是javascript引擎做了隐式类型转换。隐式类型转换可以认为javascript引擎调用的相应的类型转换函数来进行类型转换,age + ' years old'其实等价于age.toString() + 'years old',隐式类型转换和显式类型转换其实是一回事情。
  本文详细说明javascript各种类型之间如何进行类型转换,我们会介绍一些通用的转换方式,然后介绍具体转换到某种类型的方法。


1. 通用的转换方式


1.1 对象类型转换成原始类型


  javascript的对象都继承两个用于类型转换的方法toStringvalueOf
  valueOf方法将对象类型转换成原始值,如果没有被重写,valueof默认(Object.prototype.valueOf)返回对象本身。Number/String/Boolean对象重写了valueOf方法,在Chrome DevTools可以看到它们对应的原始值,如下图:



其他绝大部分对象类型都没有重写valueof。toString方法将对象转换成String型,如果没有被重写,toString方法默认(Object.prototype.toString)返回"[object type]"(type是数据类型)。Number/String/Boolean对象重写了toString方法。
  本节示例如下:

    var obj = {name: 'jerry'};
    // valueOf 默认返回对象本身
    obj.valueOf() === obj; // true
    // String对象重写了valueOf
    new String('jerry').valueOf();  // 'jerry'

    // toString 默认返回 "[object type]"
    obj.toString(); // "[object Obejct]"
    Object.prototype.toString.call(undefined); // "[object Undefined]"
    function A(){};
    new A().toString(); // "[object Object]"
    // Boolean类型重写了toString。(注意:调用之前true被包装成对象)
    true.toString(); // "true"

  toString和valueOf一般不会直接被调用,多用于隐式类型转换,我们不需要进一步详述。
  题外话:def.js中非常有趣的利用了valueOf方法,有兴趣的同学可以看下(重写valueOf方法,利用执行<<时隐式调用valueOf来实现ruby-style的继承)。

1.2 函数式调用构造函数

  用函数的方式调用对象的构造函数其实是做类型转换, 如:String(a)是把a转换成String型,而new String(a)是用a来构造一个String对象。我们一下子有了一大票类型转换函数,转换成String型的String(obj)方法, 转换成Number型的Number(obj), 转换成Boolean型的Boolean(obj)方法等等。
  本节示例如下:

    // 函数式调用构造函数其实在做类型的转换
    String(30); // "30"
    Number('123'); // 123
    Boolean(0); // false

    // 函数式调用 vs new
    typeof String(30);  // "string"
    typeof new String(30); // "object"

  对于String/Number/Boolean等函数针对不同类型的参数转换得到怎样的结果,会在下一章转换到具体类型中详细说明。

2. 转换到具体类型

2.1 String

2.1.1 obj.toString

  我们在对象类型转换成原始类型节,已经说明对象的toString方法可以将对象转换成String型,Number/Boolean型可以自动包装成对象然后调用toString方法,但是Null/Undefined类型无法调用toString方法。
  Number对象的toString方法可以接受一个进制参数,指定数字以何种进制(2进制/8进制/16进制...)的表示。
  本节示例如下:

    // Number/Boolean型自动包装成对象然后调用toString方法
    (123).toString();   // "123"
    true.toString();    // "true"
    ({name: 'jerry'}).toString(); // "[object Object]"
    // toString(radix) - 数字指定进制的字符串表示
    (123).toString(2); // "1111011"
    (123).toString(16); // "7b"
    // null/undefined无法调用toString方法
    null.toString();    // TypeError: Cannot read property 'toString' of null
    undefined.toString(); // TypeError: Cannot read property 'toString' of undefined

2.1.2 String(obj)

  我们在函数式调用构造函数节,已经说明用函数的方式调用String构造函数,可以将其他类型转换成String型。因为待转换的数据是通过参数传入的,因而能够处理Null/Undefined类型。关于不同类型转换成String型的结果,详情请参见es规范9.8节
  本节示例如下:

    String(123);   // "123"
    String(true);    // "true"
    String({name: 'jerry'}); // "[object Object]"
    String(null);    // "null"
    String(undefined); // "undefined"

2.1.3 "" + obj

  "" + obj利用隐式类型转换将obj转换成String型。当obj""+操作时,左操作数("")是String型,右操作数需要也是String型,如果obj不是String型,obj会被隐式类型转换为String型。隐式类型转换其实和调用String(obj)效果相同。
  本节示例如下:

    "" + 123;   // "123"
    "" + true;    // "true"
    "" + {name: 'jerry'}; // "[object Object]"
    "" + null;    // "null"
    "" + undefined; // "undefined"

2.1.4 numObj.toPrecision/toFixed/toExponential

  数值对象有一些方法,用于将数据类型转换成指定格式的字符串。Number.prototype.toPrecision指定数字精度, Number.prototype.toFixed指定小数点后的位数,Number.prototype.toExponential指定以指数形式表示数字。
  本节示例如下:

    // toPrecision指定数字的数量,会进行四舍五入,不够补零
    (5.125).toPrecision(3); // "5.13"
    (25).toPrecision(5); // "25.000"

    // toFixed指定小数的数量,会进行四舍五入,不够补零
    (3.2287).toFixed(3); // "3.229"
    (25).toFixed(5); // "25.00000"

    // toExponential指定为指数形式表示数字,并能指定小数点后位数
    778.1234.toExponential(2); // "7.78e+2"

2.1节小结:

推荐使用"" + obj的方式来将其他类型转换成String类型。如果将Number型转换成指定格式的字符串可以使用obj.toString(radix)的方式或numObj.toPrecision/toFixed/toExponential

2.2 Number

2.2.1 Number(obj)

  我们在函数式调用构造函数节,已经说明用函数的方式调用Number构造函数,可以将其他类型转换成Number型。关于不同类型转换成Number型的结果,详情请参见es规范9.3节。本节示例如下:

    // String to Number
    Number("123.4"); // 123.4
    Number(""); // 0
    // Boolean to Number
    Number(true);   // 1
    Number(false);  // 0
    // Object to Number
    Number({}); // NaN
    // Null to Number
    Number(null);   // 0
    // Undefined to Number
    Number(undefined); // NaN

2.2.2 +obj

一元操作符+把它的操作数转换成Number类型。+obj其实和调用Number(obj)效果相同。 本节示例如下:

    // String to Number
    +"123.4"; // 123.4
    +"123.4a"; // NaN
    +""; // 0
    // Boolean to Number
    +true;   // 1
    +false;  // 0
    // Object to Number
    +{}; // NaN
    // Null to Number
    +null;   // 0
    // Undefined to Number
    +undefined; // NaN

2.2.3 parseInt/parseFloat

  parseInt/parseFloat专门用于将String类型转换成Number类型。在Number(obj)节,我们知道Number("")值为0,而parseInt("")值为NaN,更加合理,更加符合直觉。parseInt/parseFloat是按照字符顺序一个个parse的,因此能够正确处理"123.4a"这样的String, 在Number(obj)我们知道Number("123.4a")值为NaN,而parseFloat("123.4a")值为123.4。parseInt/parseFloat接受的是String类型参数,如果参数是Boolean/Null/Undefined/Object类型,会被隐式转换成String型后,再逐字符parse,结果自然为NaN。parseInt还有第二个参数radix,用来指定进制(2/8/16/...进制),详情请见mdn parseInt。 本节示例如下:

    // parse String类型
    parseFloat("123.4");  // 123.4
    parseFloat("123.4a"); // 123.4
    parseInt("");       // NaN
    parseInt("110", 2); // 6
    // parse非String类型,会先隐式转换成String型,再parse
    parseInt(true); // NaN
    parseInt({});   // NaN
    parseInt(null); // NaN
    parseInt(undefined);     // NaN

2.2节小结:

如果要把String转换成Number型,推荐使用parseInt/parseFloat。如果要把未知类型转换成Number型,推荐使用+obj


2.3 Boolean

2.3.1 Boolean(obj)

  我们在函数式调用构造函数节,已经说明用函数的方式调用Boolean构造函数,可以将其他类型转换成Number型。关于不同类型转换成Boolean型的结果,详情请参见es规范9.2节。本节示例如下:

    // String转换成boolean, 空字符串为false, 其他为true
    Boolean(""); // false
    Boolean("jerry"); // true
    // Number转换成Boolean, 0/NaN为false,其他都为true
    Boolean(0);     // false
    Boolean(NaN);   // false
    Boolean(123);   // true
    // Object转换成Boolean,都为true
    Boolean({});    // true
    // Null to Boolean
    Boolean(null);   // false
    // Undefined to Boolean
    Boolean(undefined); // false

2.3.2 !!obj

  逻辑非操作符!先将操作数obj转换成Boolean型,如果转换的结果为true,则返回false,否则返回true,详情请参见es规范11.4.9节。显然,如果对操作数obj执行逻辑非操作符!的结果再执行逻辑非操作符!操作,就是将obj转换成了Boolean型。!!obj其实和调用Boolean(obj)效果相同。本节示例如下:

    // String转换成boolean, 空字符串为false, 其他为true
    !!""; // false
    !!"jerry"; // true
    // Number转换成Boolean, 0/NaN为false,其他都为true
    !!0;     // false
    !!NaN;   // false
    !!123;   // true
    // Object转换成Boolean,都为true
    !!{};    // true
    // Null to Boolean
    !!null;   // false
    // Undefined to Boolean
    !!undefined; // false

2.3节小结:

推荐使用!!obj的方式来将其他类型转换成Boolean类型。


3. 总结

  当javascript引擎试图去操作错误的数据类型时,引擎自动会其转换成正确的数据类型,这个过程就是隐式类型转换。我们也可以通过函数调用或其他操作符进行显示类型转换。
  其他类型转换成String类型,推荐使用"" + obj。其他类型转换成Number类型推荐使用+obj,如果是将String型转换成Number型推荐使用parseInt/parseFloat。其他类型转换成Boolean类型推荐使用!!obj
  进一步了解Javascript类型系统:

本文来自网易实践者社区,经作者魏文庆授权发布。