JavaScript的数据类型转换

简介

JavaScript是一种松散类型。这并不意味着它就没有数据类型,只是变量的值或者 JavaScript 对象属性不需要为其分配特定类型的值,也不必总是保持相同的类型。JavaScript 可以自由地将一种类型的值转为他们使用变量的上下文中需要的类型。

JavaScript 的松散类型以及轻松的类型转换仍然拯救不了程序员们需要思考他们正在处理的值的实际类型。一个浏览器脚本中非常常见的错误就是,读取一个表单空间的 value 属性,用户应该在其中输入一个数字,然后将该值与另一个数字相加。因为表单空间的 value 属性其实是字符串(尽管其包含的字符序列表示的是一串数字),如果尝试将这串字符串与另一个值相加,即使另一个值碰巧是一个数字,结果也会导致第二个值被类型转换成一个字符串,然后拼接到第一个字符串的末尾。

这个问题源于 + 操作符的数字相加和字符串拼接的双重性质。因此,这个操作符的实际作用取决于上下文,只有操作符两边的操作数都是数字时, + 操作符才用作数值相加。除此之外,它都会将操作数转换成字符型,然后执行字符串拼接。

to Boolean

在计算 if 表达式的时候,JavaScript 解释器会自动将表达式的运算结果转换为布尔类型。还有一些其他的操作符会将操作数转换为布尔型,包括逻辑操作符:&&||! 。通常用 || 来得到一个表达时的布尔型转换值。

1
var boolValue = !!x;

下面的表格也是用 || 来生成各种类型对应的布尔型。

还有一种生成布尔型值的方法是将值作为参数传入 Boolean 构造函数,这样得到的是一个布尔类型的包装对象而不是一个布尔的初始值了:

1
var boolValue = Boolean(x);
-1.6-0+011.681616.8123e-2-Infinity+InfinityNaN
!!coltruefalsefalsetruetruetruetruetruetruetruetruefalse

数字转换成 bool 型时,0 为 false ,其他所有值都是 true。特殊数字 NaN 除外,它表示将其他类型转换为数字型时,转换无效不能得到有意义的数字。NaN 始终是 false 。Infinity-Infinity 都是 true。

“” (empty string)“-1.6”“0”“1”“1.6”“8”“16”“16.8”“123e-2”“010” (Octal)“0x10” (Hex)“0xFF” (Hex)“-010”“-0x10”“xx”
!!colfalsetruetruetruetruetruetruetruetruetruetruetruetruetruetrue

字符串转布尔型更简单,除空字符串以外,都是 true,空字符串是 false 。

undefinednulltruefalsenew Object()function(){ return; }
!!colfalsefalsetruefalsetruetrue

对于其他类型, undefinednull 转换为 false ,对象和函数始终是 true 。

这是类型转换为布尔型最有价值的方面,因为它允许脚本区分属性是未定义还是指向一个对象。经常用 if 语句将要访问的对象包裹起来测试对象是否未定义,以此来避免出错,如果对象未定义,则返回 false,如果已存在,则返回 true。

1
2
3
if(document.documentElement){
scrollX = document.documentElement.scrollLeft;
}

to String

上面讲到了,类型转换为字符串经常是由 + 操作符在其中一个操作数不是数字的情况下导致的。转换为字符传类型最简单的办法就是用该值拼接一个空字符串。我们用这个方法来生成下面的表格。

另一种方法是用 String 构造函数,这样得到的不是一个字符型的初始值,而是一个 String 类型的包装对象:

1
var stringValue = String(x);
-1.6-0+011.681616.8123e-2-Infinity+InfinityNaN
“” + col-1.60011.681616.81.23-InfinityInfinityNaN

注意,上面表格中的源码 123e-2 转换后得到了字符串 1.23 ,这是因为 JavaScript 内部数字的字符串表示形式,然而 JavaScript 内部数字表示采用了 IEEE 标准规范的双精度浮点数字形式,这意味着 JavaScript 无法精确地表示所有数字。数学运算的结果可能产生近似值,当他们转换成字符串时,字符串可能出乎意料地表示为近似值。经常使用自定义函数来生成指定格式的数字字符串,类型转换机制很少适用于用来生成用于表示的数字输出。

undefinednulltruefalsenew Object()function(){ return; }
“” + colundefinednulltruefalse[object Object]function(){ return; }

当对象或者函数被转换成字符串时,会调用它们的 toString 方法。这个方法默认继承于 Object.prototype.toStringFunction.prototype.toString,但是可以通过给对象/函数的 “toString” 属性赋值一个方法来重写。函数类型转换为字符串不一定会得到函数的源代码。Function.prototype.toString 因具体实现会有很大的不同,“宿主对象” 和方法(由环境提供的对象和方法,例如 DOM元素)也是如此。

to Number

转换为数字类型,尤其是字符串转为数字,是非常常见的需求,有很多方法可用。除连接/加法操作符以外的任何数学运算操作符都会强制类型转换。所以字符串转数字可能要对数字字符串执行不影响结果数值的数学运算,例如减去 0 或者乘以 1。

1
2
3
4
5
var numValue = stringValue - 0;
/* or */
var numValue = stringValue * 1;
/* or */
var numValue = stringValue / 1;

然而,一元的 + 运算符也可以将操作数转换成数字,因为它不会做任何额外的数学运算,这是将字符串转换为数字的最快的方法。

顺便说一下,一元的 - (减法)操作符除了随后会取负其值以外,也会对操作数执行类型转换(必要的情况下)。

1
var numValue = (+stringValue);

同样也可以调用 Number 构造函数,将字符串作为够攒函数的参数,返回一个数字的包装对象:

1
var numValue = Number(stringValue);

Number 构造函数是类型转换方法中最慢的一种,当速度不是最重要的考虑因素时,它的代码逻辑会比较清晰。

下面的表格使用一元 + 运算符生成。

“” (empty string)“-1.6”“0”“1”“1.6”“8”“16”“16.8”“123e-2”“010” (Octal)“0x10” (Hex)“0xFF” (Hex)“-010”“-0x10”“xx”
+col0-1.6011.681616.81.231016255-10NaNNaN

字符串转数字时最重要的考虑是结果可能不是数字。空字符转换成数字 0,取决于应用,这样做可能会带来危害。在其他上下文中,遵循 JavaScript 八进制数字格式的字符串(0 开头)可能会有问题,类型转换会将它们按10进制数字看待。然而遵循16进制数字格式的字符串(ox 或者 oX 开头)会读作 16 进制。不能被读作数字的字符串会被类型转换为 NaNNaN 的测试方法是另其不等于自身。指数计数格式("123e-2")的数字字符串与前导的负号一起被转换。

undefinednulltruefalsenew Object()function(){ return; }
+colNaN010NaNNaN

对象和函数始终转换成 NaNundefined 也是如此,但是 null 会类型转换为 0。可能是因为它先转换为布尔型,然后转换为数字的,null 先转换成 falsefalse 会转换成数字 0。几乎不可能会出现需要将这些类型转换成数字的情况。

Parsing to Number

另一种转换字符串为数字的方法是使用一些设计用来返回一个数字的全局函数。parseFloat 函数接收一个字符串参数,将字符串解析为一个浮点数,并返回该浮点数。非字符参数会先按上面说的方式转换为字符串。

字符串解析函数会逐个字符地读取字符串直到遇到一个不能转换为数字的字符为止,这是它会停下来,并将已解析的可转换为数字的字符转换为数字,然后返回该数字。这个特性在某些情况下非常有用,例如,有一个代表 CSS 长度的值 34.5emparseFloat 会自动忽略 em ,因为这些字符不能与前面的数字字符合并转为数字。返回的值会是 34.5,CSS 字符串截取后不包含单位的数字部分。

parseFloat

“” (empty string)“-1.6”“0”“1”“1.6”“8”“16”“16.8”“123e-2”“010” (Octal)“0x10” (Hex)“0xFF” (Hex)“-010”“-0x10”“xx”
parseFloat(col)NaN-1.6011.681616.81.231000-100NaN

parseFloat将空字符串以及不能进行数字解释的字符串转换为 NaN 。指数计数法格式可以被理解,八进制格式中的前导 0 不会妨碍字符串作为十进制数来解释。十六进制字符串解释为数字 0 ,因为后面跟着 x 不能解释为数字的一部分,所以解析在前导的 0 之后就结束了。

undefinednulltruefalsenew Object()function(){ return; }
parseFloat(col)NaNNaNNaNNaNNaNNaN

非字符串值会先被 parseFloat 转换为字符串。因为类型转换为字符串不会得到一个能解释为数字的字符串,所以结果是 NaN。对象和函数可能有自定义的 toString 方法,可能会返回能够进行数字解释的字符串,但那是非常规的需求。

parseInt

parseInt 函数与 parseFloat 的作用非常相似,不同之处在于它尝试将字符串参数解释为整数,因此可以识别较少的字符作为该数字的一部分。

parseInt 偶尔被用于将一个浮点数转换为整数。它非常不适合这样用,因为如果它的参数是数字类型,它会先被转换成一个字符串,然后在转换成数字,非常低效。 2e-200这样的数字 可能会产生错误的结果 ,近似的整数是 0,但是 parseInt 返回了 2。由于 JavaScript 使用的数字格式,数字经常被表示为近似值。所以,例如 1/2+1/3+1/6 = 0.99999999999999,如果用 parseInt 来转换该表达式,会返回 0。

对于将数字舍入为整数,优先使用Math.roundMath.ceilMath.floor 这三个方法。

-1.6-0+011.681616.8123e-2-Infinity+InfinityNaN
parseInt(col)-10011816161NaNNaNNaN

当它作用于数字时,参数的初始值类型转换为字符串的过程在结果中体现地很明显。注意,123e-2 的内部数字是 1.23 ,类型转换为字符串得到 "1.23",上面表格中的转换结果看上去奇怪,其实是正确的。

“” (empty string)“-1.6”“0”“1”“1.6”“8”“16”“16.8”“123e-2”“010” (Octal)“0x10” (Hex)“0xFF” (Hex)“-010”“-0x10”“xx”
parseInt(col)NaN-101181616123816255-8-16NaN

八进制和十六进制数字格式的字符串的确表示整数,并且parseInt能够根据 JavaScript 的源码解释它们,即使是以负号开头。

undefinednulltruefalsenew Object()function(){ return; }
parseInt(col)NaNNaNNaNNaNNaNNaN

由于 parseInt 会将非字符型参数类型转换为字符串,所以对于 booleannullundefined 对象和函数类型的参数来说,parseInt 得到的结果总是与 parseFloat 相同(假设对象和函数没有自定义 toString 方法)。

有一个基数参数的 parseInt

很少会允许 parseInt 推导出字符串中数字表示的基数,因为前导的 0 很少用于指示八进制格式的数据(尤其是在用户输入中)。为了解决这个问题,parseInt 能够识别第二个指示基数的参数,可以用于指定解释字符中数字的基数。第二个参数指定为 10 会导致 parseInt 以 10 为基数解释字符。

“” (empty string)“-1.6”“0”“1”“1.6”“8”“16”“16.8”“123e-2”“010” (Octal)“0x10” (Hex)“0xFF” (Hex)“-010”“-0x10”“xx”
parseInt(col, 10)NaN-1011816161231000-100NaN

八进制格式的字符串现在可以以 10 为基数来解释,16 进制字符串则只能转换为 0 ,因为遇到 "x" 的时候,转换就会停止。

parseInt 可以用于以 2 到 36 为基数的数字。下面是以 16 为基数。

“” (empty string)“-1.6”“0”“1”“1.6”“8”“16”“16.8”“123e-2”“010” (Octal)“0x10” (Hex)“0xFF” (Hex)“-010”“-0x10”“xx”
parseInt(col, 16)NaN-10118222246701616255-16-16NaN

十六进制的 0x 格式在以 16 为解释基数时可以被识别。

最后以 3 为基数:

-1.6-0+011.681616.8123e-2-Infinity+InfinityNaN
parseInt(col, 3)-10011NaN111NaNNaNNaN

数字参数转换为字符串的结果很明显。数字 8 转换为 NaN 是因为 "8" 这个字符不能以 3 为基数解释,得到一个为空的可接受字符的序列,产生与空字符串一样的结果。

“” (empty string)“-1.6”“0”“1”“1.6”“8”“16”“16.8”“123e-2”“010” (Octal)“0x10” (Hex)“0xFF” (Hex)“-010”“-0x10”“xx”
parseInt(col, 3)NaN-1011NaN115300-30NaN

ToInt32

ToInt32 是一个只在 JavaScript 实现中可用的内部函数,不能在脚本中以 parseInt 一样的方式直接调用。JavaScript 转换为数字时很少用到这个函数,但是一些情况下可以用到。按位操作符,例如按位或(|)和 按位与(&)操作符作用于数字,所以它们会将操作数转换为数字。然而它们只能作用于 32 位有符号整型,所以对于给定的(可能是转换得到的)数字,它们会将它作为参数传入 ToInt32 函数,用返回值作操作数。返回的数字始终是一个 32 位有符号整型。

这种效果就像 parseInt 结合了类型转换为数字。然而结果只能限定在 32 位范围内,而且始终是数字,不可能是 NaN 或者 ±Infinity

就像在对结果数值没有影响的操作中使用数学运算符一样,也可以在对 ToInt32 返回的结果没有影响的操作中使用按位操作符。下面的表格就是用 按位或 0 操作生成的。

-1.6-0+011.681616.8123e-2-Infinity+InfinityNaN
col\0-10011816161000

NaN±Infinity 变成了 0 ,浮点数被截取成了整数。

“” (empty string)“-1.6”“0”“1”“1.6”“8”“16”“16.8”“123e-2”“010” (Octal)“0x10” (Hex)“0xFF” (Hex)“-010”“-0x10”“xx”
col\00-10118161611016255-1000

类型转换为 NaN 的字符串会从 ToInt32 中返回 0 。

undefinednulltruefalsenew Object()function(){ return; }
col\0001000

甚至 undefined ,对象和函数都被这个操作转换为 0 。注意 布尔值 true 被转换成数字 1。

转换用户输入

多数获得用户输入的机制,例如 <input type=text>prompt 这些,提供了字符串格式的输入结果。如果希望用户输入一个数字,他们仍然可以输入任何东西(至少他们可能打错字)。如果字符串需要转换成数字用于后续的操作,上面提到的方法可以根据哪个最接近期待的用户输入来选择,但是一些由错误的输入得到的结果可能会难以识别和处理。

正则表达式示例

1
2
3
4
5
6
7
8
9
/^\d+$/                 //All-digit
/^\s*[-+]?\d+\s*$/ //Unbroken Signed integer & spaces
/^\d{1,5}$/ //1 to 5 digits
/^\d+\.\d\d$/ //Money
/^\d+(\.\d{2})$/ //Money
/^\d{1,3}(,\d\d\d)*\.\d\d$/ //comma-separated money - 12,432.57

// optional comma-separated money - 12,432.57 or 12432.57
/^\d{1,3}(,\d\d\d)*\.\d\d$|^\d+\.\d\d$/

本文标题:JavaScript的数据类型转换

文章作者:kinboy

发布时间:2018年12月10日 - 21:37:56

最后更新:2019年07月15日 - 18:05:10

原始链接:http://kinboyw.github.io/2018/12/10/JavaScript的数据类型转换/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

------ Passage Ending ------