JavaScript 网页编程从入门到精通 (清华社"视频大讲堂"大系·网络开发视频大讲堂)
上QQ阅读APP看书,第一时间看更新

4.5 数据类型转换

JavaScript能够根据表达式运算需要自动转换数据类型,用户也可以根据需要手动转换数据类型,用法比较灵活。常用数据类型转换包括:把值转换为字符串,把字符串转换为数字,或者把值转换为布尔值,更复杂的类型转换是值类型与引用型数据之间的相互转换。

4.5.1 值类型转换

JavaScript能够自动转换变量的数据类型。这种转换都是隐性的和自动的,不需要手动设置。

例如,当使用alert()方法时,JavaScript会自动把所有类型的值转换为字符串。如果在逻辑表达式中,则会自动把值转换为布尔值,转换的规则则事先已经定好,如空字符串和数字0为false、实字符串和其他数字都被转换为true等。

在自动转换中,JavaScript一般遵循的原则是:根据运算的类型环境,按需要进行转换。

例如,如果在执行字符串连接操作时,则会把数字转换为字符串;如果在执行基本数学运算时,则会尝试把字符串转换为数值;如果在逻辑运算环境中,则会尝试把值转换为布尔值等。

如表4-5所示,是常见值在不同环境中被自动转换的值列表。

表4-5 数据类型自动转换列表

4.5.2 引用型转换

表4-5介绍了把对象转换为原始值的基本方法,但是如何转换,以及转换的结果都没有说明,下面结合几个很容易忽略的问题进行详细解释。

1.对象在逻辑运算环境中的转换

如果把非空对象用在逻辑运算环境中,则对象被转换为true。这包括所有类型的对象,即使是值为false的包装对象也为true。

【示例1】下面的代码创建了3个不同类型的对象,然后在逻辑与运算中,可以看到它们全部为true。

      var a = new Boolean(false);
      var b = new Number(0);
      var c = new String("");
      a&&alert(a);                       //a转换为布尔值为true,但是提示它的字符串转换值为"false"
      b&&alert(b);                       //b转换为布尔值为true,但是提示它的字符串转换值为"0"
      c&&alert(c);                       //c转换为布尔值为true,但是提示它的字符串转换值为""

2.对象在数值运算环境中的转换

如果对象用在数值运算环境中,则对象会被自动转换为数字,如果转换失败,则返回值NaN。具体转换过程如下。

首先,调用对象的valueOf()方法,返回对象自身的值。大多数对象都继承了Object对象的valueOf()。valueOf()方法仅能够返回自身值,但是不会转换值的类型,所以valueOf()方法取出的值并非都是数值。

如果对象自身值不为数值,就会调用对象的toString()方法,把对象自身值转换为字符串。然后调用parseInt()或parseFloat()函数把字符串转换成数字。

如果上述方法不能够成功,JavaScript会尝试通过强制方法把对象转换为数值。如果成功则已,不成功就返回NaN。

【示例2】下面的代码是使用Boolean()构造器将布尔值true转换为布尔型对象,然后再通过a -0数值运算,将布尔型对象转换为数字1。

      var a=new Boolean(true);               //把true封装为对象
      alert(a.valueOf());                    //测试该对象的值为true
      alert(typeof(a.valueOf()));            //测试值的类型为boolean
      a=a-0;                                 //投放到数值运算环境中
      alert(a);                              //返回值为1
      alert(typeof a);                       //再次测试它的类型,则为number

JavaScript自动转换对象a到数字的过程如下。

首先,直接使用valueOf()方法取值,没有成功。

      a=a.valueOf();                                      //取出对象自身值
      alert(a);                                           //返回true
      alert(typeof a);                                    //返回类型为boolean

然后,把对象自身值转换为字符串,再次尝试转换,没有成功。

      a=a.valueOf();                                      //取出对象自身值
      a=a.toString();                                     //转换为字符串,返回"true"
      a=parseFloat(a);                                    //转换为数值
      alert(a);                                           //返回为NaN
      alert(typeof a);                                    //返回类型为number

最后,尝试强制转换,则成功。

      a=a.valueOf();                                      //取出对象自身值
      a=Number(a);                                        //强制转换
      alert(a);                                           //返回1
      alert(typeof a);                                    //返回类型为number

3.数组在数值运算环境中的转换

当数组被用在数值运算环境中时,数组将根据包含的元素来决定转换的值。

如果为空数组,则被转换为数值0。当数组为空时,JavaScript将调用toString()方法把数组转换为空字符串,然后再将空字符串强制转换为数值0。

如果数组仅包含一个数字元素,则被转换为该数字的数值。例如:

      var a = [5];
      a=a*1;                                           //投放到数值运算环境中
      alert(a);                                        //返回数值5
      alert(typeof a);                                 //返回类型为number

如果数组包含多个元素,或者仅包含一个非数字元素,则返回NaN。例如:

      var a = [true];
      a = a * 1;
      alert(a);                                            //返回数值NaN
      alert(typeof a);                                     //返回类型为number

4.对象在模糊运算环境中的转换

当对象用于字符串环境中时,JavaScript能够调用toString()方法把对象转换为字符串再进行相关计算。而在数值运算环境中时,则会根据上面两小节介绍的方法进行转换操作。但是,在JavaScript中有两处运算环境比较模糊:加号运算符和比较运算符。当值进行加号运算或者比较运算时,既可以作用于数值,也可以作用于字符串。

当对象与数值进行加号运算时,则会尝试把对象转换为数值,然后参与求和运算。如果不能够转换为有效数值,则执行字符串连接操作。例如:

      var a=new String("a");                                //字符串封装为对象
      var b=new Boolean(true);                              //布尔值封装为对象
      a=a+0;                                                //加号运算
      b=b+0;                                                //加号运算
      alert(a);                                             //返回字符串"a0"
      alert(b);                                             //返回数值1

当对象与字符串进行加号运算时,则直接转换为字符串,进行连接操作。例如:

      var a = new String(1);
      var b = new Boolean(true);
      a = a + "";
      b = b + "";
      alert(a);                                            //返回字符串"1"
      alert(b);                                            //返回字符串"true"

当对象与数值进行比较运算时,则会尝试把对象转换为数值,然后参与比较运算。如果不能够转换为有效数值,则执行字符串比较运算。例如:

      var a=new String("true");                              //无法转换为数值
      var b=new Boolean(true);                               //可以转换为数值1
      a = a > 0 ;
      b = b > 0 ;
      alert(a);                                              //返回false,以字符串形式进行比较
      alert(b);                                              //返回true,以数值形式进行比较

当对象与字符串进行比较运算时,则直接转换为字符串,进行比较操作。

对于Date对象来说,加号运算符会先调用toString()方法进行转换。因为当加号运算符作用于Date对象时,一般都是字符串连接操作。而当比较运算符作用于Date对象时,则会转换为数字以便比较时间的先后。

4.5.3 转换为字符串

把值转换为字符串是编程中的常见行为,手动转换的具体方法如下。

1.使用加号运算符

当值与空字符串相加运算时,JavaScript会自动把值转换为字符串。例如:

把数字转换为字符串:

      var a = 123456;
      a = a + "";
      alert(typeof a);                                     //返回类型为string

把布尔值转换为字符串,返回字符串"true"或"false":

      var a = true;
      a = a + "";
      alert(a);                                            //返回字符串"true"

把数组转换为字符串,返回数组元素列表,以逗号分隔:

      var a = [1,2,3];
      a = a + "";
      alert(a);                                            //返回字符串"1,2,3"

把函数转换为字符串,返回函数结构的代码字符串:

      var a = function(){
          return 1;
      };
      a = a + "";
      alert(a);                                            //返回字符串"var a=function(){return 1; }"

如果把JavaScript内置对象转换为字符串,则只返回构造函数的基本结构代码,而自定义的构造函数,则与普通函数一样,返回函数结构的代码字符串。

      a = Date + "";
      alert(a);                                           //返回字符串"function Date(){[native code]}"

如果内置对象为静态函数,则返回字符串不同。例如:

      a = Math + "";
      alert(a);                                          //返回字符串"[object Math]"

如果把对象实例转换为字符串,则返回的字符串会根据不同类型或定义对象的方法和参数而不同,具体说明如下。

对象直接量,则返回字符串为"[object object]"。

      var a = {
          x :1
      }
      a = a + "";
      alert(a);                                        //返回字符串"[object object]"

如果是自定义类的对象实例,则返回字符串为"[object object]"。

      var a =new function(){}();
      a = a + "";
      alert(a);                                        //返回字符串"[object object]"

如果是内置对象实例,具体返回字符串必须根据传递的参数而定。

【示例】正则表达式对象会返回匹配模式字符串,时间对象会返回当前GMT格式的时间字符串,数值对象会返回传递的参数值字符串或者0等。

      a = new RegExp(/^\w$/) + "";
      alert(a);                                        //返回字符串"/^\w$/"

【拓展】加号运算符有两个计算功能:数值求和、字符串连接。但是字符串连接操作的优先级要大于求和运算。因此,在可能的情况下,即运算元的数据类型不一致时,加号运算符会尝试把数值运算元转换为字符串,再执行连接操作。

但是当多个加号运算符位于同一行时,这个问题就比较复杂,例如:

      var a = 1 + 1 + "a";
      var b = "a" + 1 + 1 ;
      alert(a);                                        //返回字符串"2a"
      alert(b);                                        //返回字符串"a11"

通过上面示例可以看到,加号运算符不仅仅优先于连接操作,同时还会考虑运算的顺序。对于变量a来说,按照从左到右的运算顺序,加号运算符会执行求和运算,然后再执行连接操作。但是对于变量b来说,由于"a" + 1表达式运算将根据连接操作来执行,所以返回字符串"a1",然后再用这个字符串与数值1进行运算,再次执行连接操作,最后返回字符串"a11",而不是字符串"a2"。

如果要避免此类现象的发生,可以考虑使用小括号运算符来改变一行内表达式的运算顺序。例如:

      var b="a"+(1+1);                                //返回字符串"a2"

2.使用toString()方法

当为原始值调用toString()方法时,JavaScript会自动把它们装箱为对象。然后再调用toString()方法,把它们转换为字符串。例如:

      var a = 123456;
      a.toString();
      alert(a);                                      //返回字符串"123456"

使用加号运算符转换字符串,实际上也是调用toString()方法来完成。只不过是JavaScript自动调用toString()方法实现的。

4.5.4 案例:转换数字模式

Number扩展了toString()方法,允许传递一个整数参数,该参数可以设置数字的显示模式。数字默认为十进制显示模式,通过设置参数可以改变数字模式。

(1)如果采用默认模式,则toString()方法会直接把数值转换为数字字符串。例如:

      var a = 1.000;
      var b = 0.0001;
      var c = 1e-4;
      alert(a.toString());                                //返回字符串"1"
      alert(b.toString());                                //返回字符串"0.0001"
      alert(c.toString());                                //返回字符串"0.0001"

toString()方法能够直接输出整数和浮点数,保留小数位。小数位末尾的零会被清除。但是对于科学计数法,则在条件许可的情况下把它转换为浮点数,否则就使用科学计数法方式输出字符串。例如:

      var a = 1e-14;
      alert(a.toString());                                 //返回字符串"1e-14; "

在默认模式下,无论数值采用什么模式,toString()方法返回的都是十进制的数字。因此,对于八进制、二进制或十六进制数值,toString()方法都会先把它们转换为十进制数值之后再输出。例如:

      var a=010;                                           //八进制数值10
      var b=0x10;                                          //十六进制数值10
      alert(a.toString());                                 //返回字符串"8"
      alert(b.toString());                                 //返回字符串"16"

(2)如果设置参数,则toString()方法会根据参数把数值转换为对应进制的值之后再输出。例如:

      var a=10;                                            //十进制数值10
      alert(a.toString(2));                                //返回二进制数字字符串"1010"
      alert(a.toString(8));                                //返回八进制数字字符串"12"
      alert(a.toString(16));                               //返回二进制数字字符串"a"

4.5.5 案例:设置数字转换为字符串的小数位数

使用toString()方法把数值转换为字符串时,无法保留小数位,这在货币格式化、科学计数等专业领域输出显示数字是不方便的。从1.5版本开始,JavaScript新定义了3个新方法:toFixed()、toExponential()和toPrecision()。

1.toFixed()

toFixed()能够把数值转换为字符串,并显示小数点后的指定位数。例如:

      var a = 10;
      alert(a.toFixed(2));                                //返回字符串"10.00"
      alert(a.toFixed(4));                                //返回字符串"10.0000"

2.toExponential()

toFixed()方法不采用科学计数法,但是toExponential()方法专门用来把数字转换为科学计数法形式的字符串。例如:

      var a = 123456789;
      alert(a.toExponential(2));                           //返回字符串"1.23e+8"
      alert(a.toExponential(4));                           //返回字符串"1.2346 e+8"

toExponential()方法的参数指定了保留的小数位数。省略的部分采用四舍五入的方法进行处理。

3.toPrecision()

toPrecision()方法与toExponential()方法不同,它是指定有效数字的位数,而不仅仅是指小数位数。例如:

      var a = 123456789;
      alert(a.toPrecision(2));                             //返回字符串"1.2e+8"
      alert(a.toPrecision(4));                             //返回字符串"1.235 e+8"

4.5.6 转换为数字

JavaScript提供了两种静态方法把非数字的原始值转换为数字:parseInt()和parseFloat()。其中parseInt()可以把值转换为整数,而parseFloat()可以把值转换为浮点数。

parseInt()和parseFloat()函数对字符串类型的值有效,其他类型的值调用这两个函数都会返回NaN。在转换字符串为数字之前,它们都会对字符串进行分析,以验证转换是否继续,具体分析如下。

1.使用parseInt()

在开始转换时,parseInt()函数会先查看位置0处的字符,如果该位置不是有效数字,则将返回NaN,不再深入分析。如果位置0处的字符是数字,则将查看位置1处的字符,并进行同样的测试,依此类推,在整个验证过程中,直到发现非数字字符为止,此时parseInt()函数将把前面分析合法的数字字符转换为数值,并返回。例如:

      alert(parseInt("123abc"));                           //返回数字123
      alert(parseInt("1.73"));                             //返回数字1
      alert(parseInt(".123"));                             //返回值NaN

在浮点数中的点号对于parseInt()函数来说是属于非法字符的,因此不会转换它,并返回。

如果以0为开头的数字字符串,则parseInt()函数会把它作为八进制数字处理,先把它转换为数值,然后再转换为十进制的数字返回。如果以0x为开头的数字字符串,则parseInt()函数会把它作为十六进制数字处理,先把它转换为数值,然后再转换为十进制的数字返回。

      var d="010";                                        //八进制数字字符串
      var e="0x10";                                       //十六进制数字字符串
      alert(parseInt(d));                                 //返回十进制数字8
      alert(parseInt(e));                                 //返回十进制数字16

parseInt()也支持基模式,可以把二进制、八进制、十六进制等不同进制的数字字符串转换为整数。基模式由parseInt()函数的第二个参数指定。

【示例1】下面代码把十六进制数字字符串"123abc"转换为十进制整数:

      var a = "123abc";
      alert(parseInt(a,16));                               //返回值十进制整数1194684

【示例2】下面代码把二进制、八进制和十进制数字字符串转换为整数:

      alert(parseInt("10",2));                             //把二进制数字10转换为十进制整数为2
      alert(parseInt("10",8));                             //把八进制数字10转换为十进制整数为8
      alert(parseInt("10",10));                            //把十进制数字10转换为十进制整数为10

【示例3】如果第一个参数是十进制的值,包含0前缀,为了避免被误解为八进制的数字,则应该指定第二个参数值为10,即显式定义基,而不是采用默认基。

      alert(parseInt("010"));                              //把八进制数字10转换为十进制整数为8
      alert(parseInt("010",8));                            //把八进制数字010转换为十进制整数为8
      alert(parseInt("010",10));                           //把十进制数字010转换为十进制整数为10

2.使用parseFloat()函数

parseFloat()函数与parseInt()函数用法基本相同,但是它能够识别第一个出现的小数点号,而第二个小数点号被视为非法的。

      alert(parseFloat("1.234.5"));                         //返回数值1.234

此外,数字必须是十进制形式的字符串,而不能够使用八进制或十六进制的数字字符串。同时对于数字前面的0(八进制数字标识)会忽略,而对于十六进制形式的数字,则返回0值。例如:

      alert(parseFloat("123"));                            //返回数值124
      alert(parseFloat("123abc"));                         //返回数值123
      alert(parseFloat("010"));                            //返回数值10
      alert(parseFloat("0x10"));                           //返回数值0
      alert(parseFloat("x10"));                            //返回数值NaN

3.使用乘号运算符

加号运算符不仅能够执行数值求和运算,还可以把字符串连接起来。由于JavaScript处理字符串连接操作的优先级要高于数字求和运算。因此,当数字字符串与数值使用加号连接时,将优先执行连接操作,而不是求和运算。例如:

      var a=1;                                       //数值
      var b="1";                                     //数字字符串
      alert(a+b);                                    //返回字符串"11"

在执行表达式a+b的运算时,变量a先被转换为字符串,然后以求和进行计算,所以计算结果为字符串"11",而不是数值2。因此,我们常常使用加号运算符把一个值转换为字符串。

不过,如果让变量b乘以1,则加号运算符就以求和进行计算。例如:

      var a=1;                                           //数值
      var b="1";                                         //数字字符串
      alert(a+(b*1));                                    //返回数值2

如果让一个数字字符串变量乘以1,则JavaScript解释器能够自动把数字字符串转换为数值,然后再继续求和运算,而不是进行字符串连接操作。

4.5.7 转换为布尔值

在JavaScript中,任何数据都可以被自动转换为布尔值,这种转换往往都是自动完成的。例如,把值放入条件或循环结构的条件表达式中,或者参与到逻辑运算时,JavaScript解释器都会自动把它们转换为布尔值。用法可以手动进行转换,具体方法如下。

1.使用双重逻辑非

任何一个值如果在前面加上一个逻辑非运算符,JavaScript都会把这个表达式看作是逻辑运算。执行运算时,先把值转换为布尔值,然后再执行逻辑非运算。例如:

      var a=!0;                                          //返回true
      var b=!1;                                          //返回false
      var c=! NaN;                                       //返回true
      var d=! null;                                      //返回true
      var e=! undefined;                                 //返回true
      var f=! [];                                        //返回false
      var g=! {};                                        //返回false

如果再给这个表达式添加一个逻辑非运算符,所得的布尔值就是该值被转换为布尔型数据的真实值。例如:

      var a=! !0;                                          //返回false
      var b=! !1;                                          //返回true
      var c=! ! NaN;                                       //返回false
      var d=! ! null;                                      //返回false
      var e=! ! undefined;                                 //返回false
      var f=! ! [];                                        //返回true
      var g=! ! {};                                        //返回true

2.使用Boolean()构造函数转换

使用Boolean()构造函数转换的方法如下:

      var a =0;
      var b =1;
      a=new Boolean(a);                                   //返回false
      b=new Boolean(b);                                   //返回true

不过这种方法会把布尔值包装为引用型对象,而不再是原始值。使用typeof运算符检测如下:

      var a =0;
      var b = ! ! a;
      var c = new Boolean(a);
      alert(typeof b);                                       //返回boolean
      alert(typeof c);                                       //返回object

4.5.8 案例:装箱和拆箱

值类型数据与引用类型数据可以相互转换,转换时需要进行装箱和拆箱操作。

1.装箱

装箱(boxing)是指将值类型数据包装为对应的引用类型对象。例如,数值对应的包装对象为Number对象、布尔型对应的包装对象为Boolean对象、字符串对应的包装对象为String对象、正则表达式直接量对应的包装对象为RegExp对象等。

在JavaScript对象系统中,包含有String、Number、Function、Boolean 4种基本对象构造器,它们是构造JavaScript对象系统的基础。

【示例1】在本示例中,变量a和b的值都是1,但是它们属于不同数据类型,其中a为数值,而b为对象。

      var a=1;                                               //直接赋值
      var b=new Number(1);                                   //通过Number构造函数装箱后赋值
      alert(typeof a);                                       //返回number,说明其值为值类型的数值
      alert(typeof b);                                       //返回object,说明其值为引用类型的对象

JavaScript的typeof运算符能够返回6种数据类型:number、string、boolean、object、function和undefined。其中object和function为引用类型数据。

点号运算符一般要求左侧运算元为引用类型对象,因此当值类型变量进行此类操作时,JavaScript将会自动把值类型的变量进行装箱操作。

【示例2】在本示例中,为Object对象定义一个扩展方法test(),该方法能够检测当前对象的数据类型是否为Object对象的实例,当前对象的构造器是否为Number或String。

      Object.prototype.test=function(){                         //扩展Object构造器的方法f()
            alert(typeof this);                                 //显示当前对象的数据类型
            alert(this instanceof Object);                      //显示当前对象是否为对象的实例
            alert(this.constructor==Number);                    //显示当前对象的构造器是否为Number
            alert(this.constructor==String);                    //显示当前对象的构造器是否为String
      }

如果定义一个数值变量,可使用点运算符调用test()方法:

      var a=1;                                               //数值变量
      alert(typeof a);                                       //返回number数值类型
      a.test();                                              //调用检测方法

可以看到,变量a的类型为引用型对象,而不再是值类型的数值。该对象为Object对象的实例,是Number对象的实例,但不是String对象的实例。

如果定义一个字符串变量,可使用点运算符调用test()方法:

      var b="string"                                       //字符串变量
      alert(typeof b);                                     //返回string字符串类型
      b.test();                                            //调用检测方法

可以看到,变量b的类型为引用型对象,而不再是值类型的字符串。该对象为Object对象的实例,是String对象的实例,但不是Number对象的实例。

2.拆箱

拆箱(unboxing)是指将引用类型对象转换为对应的值类型对象,这主要通过valueOf()方法来实现。调用任何对象的valueOf()方法都会返回这个对象的值。对于自定义的对象,用户可以自定义它的valueOf()方法,并把它理解为对这个类型的对象的拆箱。

【示例3】在JavaScript中实现把引用类型的对象拆箱为数值对象,还可以使用toString()等方法来实现。

      var a = new Number(1);
      var b =new String("1");
      alert(typeof a);                                          //返回object类型
      alert(typeof b);                                          //返回object类型
      alert(typeof a.valueOf());                                //返回number数值类型
      alert(typeof b.valueOf());                                //返回string字符串类型
      alert(typeof a.toString());                               //返回string字符串类型
      alert(typeof b.toString());                               //返回string字符串类型

在表达式运算中,JavaScript通过调用valueOf()方法试图将引用类型的数据拆箱为值类型,然后再进行运算。

【示例4】在本示例中,当加号运算符准备计算变量a和b的和时,先把引用类型的变量b借助valueOf()方法转换为值类型,然后再进行计算,最后返回值为2。

      var a=1;                                           //值类型
      var b=new Number(1);                               //引用类型
      alert(a+b);                                        //返回值为2
      alert(typeof(a+b));                                //返回类型为number

4.5.9 案例:强制转换

JavaScript支持使用下面方法强制类型转换。

Boolean(value):把参数值转换为boolean型。

Number(value):把参数值转换为number型。

String(value):把参数值转换为string型。

【示例】在下面代码中,分别调用上述3个函数,把参数值强制转换为新的类型值。

      var a=String(true);                                   //返回字符串"true"
      var a=String(0);                                      //返回字符串"0"
      var b=Number("1");                                    //返回数值1
      var c=Number(true);                                   //返回数值1
      var d=Number("a");                                    //返回NaN
      var e=Boolean(1);                                     //返回true
      var f=Boolean("");                                    //返回false

注意:使用强制方式转换数据类型,有时会产生意想不到的情况。例如,在上面示例中,true被强制转换为数值1, Number(false)会转换为0,而使用parseInt()方法转换时,它们都返回NaN。

            var a=parseInt(true);                            //返回NaN
            var b=parseInt(false);                           //返回NaN

当要转换的值是至少有一个字符的字符串、非0数字或对象时,Boolean()函数将返回true。如果该值是空字符串、数字0、undefined或null,则它将返回false。

Number()函数的强制类型转换与parseInt()和parseFloat()方法的处理方式相似,只是它转换的是整个值,而不是部分值。例如:

      var a=Number("123abc");                        //返回NaN
      var b=parseInt("123abc");                      //返回数值123

String()函数与toString()方法功能基本相同。但是String()函数能够把null或undefined值强制转换为字符串,而不引发错误。例如:

      var a=String(null);                            //返回字符串"null"
      var b=String(undefined);                       //返回字符串"undefined"

但是下面用法都将导致异常:

      var a = null.toString();
      var b = undefined.toString();

在JavaScript中,使用强制类型转换有时会非常有用,但是应该确保转换值的正确。