前言
JS 令人头疼的一点就是它属于弱类型语言,一个变量存储的值可以是字符串、数值、布尔值或者对象等,可以随时变更。获取一个变量,你不会知道其存储的值是什么类型的,所以很多时候都需要进行类型检测。
除了手动变更类型之外,有些情况下,JS 内部也会自动进行类型转换,以满足部分操作符以及语句等的执行。
下面就是我对 JS 类型转换的一些总结,其中大部分来自《JavaScript 高级程序设计》这本书。
1. Boolean 转换
转换情况
- 调用
Boolean()
- 在
if
或while
流程控制中,内部执行相应的Boolean()
转换 - 逻辑运算符,主要是
!
和!!
,内部执行Boolean()
转换
转换规则
数据类型 | 转换为 true 的值 | 转换为 false 的值 |
---|---|---|
String | 任何非空字符串 | 空字符串"" |
Number | 任何非零数字值(包括无穷大) | 0 和 NaN |
Object | 任何对象 | null |
Undefined | 不适用 | undefined |
也就是说,只有 0 / NaN / "" / null / undefined
才会转换为 false
,其余都是 true
所以在 if
判断中,直接写变量有时候是不够严谨的
if (!a){
//=> 想要在 a 不存在或没定义的时候执行
//=> 实际上存储的值是 0 或者 '' 等也会执行
}
//=> 应该使用 typeof
if (typeof a == 'undefined') {
//=> a 不存在或没定义的时候执行
}
//=> 或者使用全等
if (a === undefined) {
//=> 使用 == 也不够严谨,因为 null == undefined
//=> 需要区分 null 或 undefined 的 情况较少
}
2. Number 转换
转换情况
- 调用
Number()
、parseInt()
、parseFloat()
- 其他情况都是使用
Number()
的转换机制
isNaN()
- 一元加或减操作符,注意:这里是一元的,如
s = + s
,区别于加法和拼接 - 递增或递减,以及加法(除了拼接)、减法、乘法、除法、求模等操作符
- 关系操作符
转换规则
Number()
:
- Boolean 值
- true
=> 1
- false
=> 0
null
=>0
undefined
=>NaN
String 值
- 只包含数字(包含正负号),将其转换为十进制数值,忽略前导零
- 包含有效的浮点格式,将其转换为对应的浮点数值,忽略前导零
- 包含有效的十六进制格式,将其转换为相同大小的十进制数值
- 空字符串
""
,空格' '
,换行符'\n'
,制表符'\t'
=>'0'
- 包含其他格式的字符串,则将其转换为
NaN
Object 值
- 调用对象的
toString()
方法,然后依照上面的规则转换返回的字符串
- 调用对象的
// [普通对象]
({}).toString() => '[object Object]' => NaN
// [数组]
[12,23].toString() => '12, 23' => NaN
[12].toString() => '12' => 12
[].toString() => '' => 0
// [正则]
/^$/.toString() => '/^$/' => NaN
// [函数]
function a() {}
a.toString() => "function a(){}" => NaN
parseInt()
:
- 第一个参数必须是字符串,如果不是则转换为字符串,使用 toString()
方法转换为字符串
- 忽略前面的空格,直至找到第一个非空字符
- 第一个非空字符不是数字字符或者负号,则返回 NaN
- 空字符串返回 NaN
- 第一个是数字字符,会继续解析下一个字符,直至遇到非数字字符,返回前面的数字字符
- 遇到非数字字符后,后面的字符都是无效的了
- 传入第二个参数:转换时使用的基数,即多少进制,就可以解析二进制、八进制、十六进制的字符串。指定了第二个参数的,字符串中甚至不用带前面的前缀,如 parseInt("AF", 16);//175
- 为了避免解析错误,任何情况下都应该明确指定基数,十进制也不例外
parseFloat()
:
- 第一个参数必须是字符串,如果不是则转换为字符串,使用 String()
方法
- 字符串中第一个小数点有效,第二个无效
- 空字符串返回 NaN
- 始终忽略前导零
- 只要遇到非浮点字符或者第二个小数点,后面的字符串都无效
- 字符串解析为整数,那么返回整数
3. String 转换
转换情况
- 基于
alert / confirm / prompt / document.write
等方法输出内容 - 调用
toString()
、String()
- 加号操作符,有一个是字符串时,内部调用
toString()
- 对象转换成 Number 类型时,内部调用
toString()
转换规则
toString()
方法
- 返回相应值的字符串表现
- 数值、布尔值、对象和字符串值都有一个 toString()
方法
- 字符串的 toString()
方法返回字符串的一个副本
- null
=> "null"
- undefined
=> "undefined"
- NaN
=> 'NaN'
- true
=> 'true'
,false
=> 'false'
- 普通对象只能返回 [object Object]
,不能返回字符串形式
- 数组 []
=> ''
,[12, 23]
=> '12,23'
- 正则、日期等对象都返回其字符串表现
String()
转型函数,你可以认为 String
和 toString
转换机制是一样的。
4. 特殊转换
+ 号操作的特殊情况
// 虽然没有看到字符串,但是引用类型转换为数字时,会先转换为字符串,这样就变成了字符串拼接
[12] + 10 //=> "1210"
({}) + 10 //=> '[object Object]10'
{} + 10 //=> 10
//=> 原因是 {} 会被解析为代码块,最后只是操作了 +10
{} + {} //=> '[object Object][object Object]'
//=> 非常特殊,不同浏览器解析有差异
({})+{} //=> '[object Object][object Object]'
{} + ({}) //=> NaN
//=> 一行中开头的 {} 才会被解析为代码块,所以用 () 包裹后可以避免被解析为代码块,一行后面的 {} 不会被解析为代码块,而是空对象。
== 进行比较时
对象 == 对象:地址相等才相等
{} == {} //=> false
[] == [] //=>false
{name: 'xxx'} == {name: 'xxx'} //=> false
var obj1 = {};
var obj2 = obj1;
obj1 == obj2 //=> true
对象 == 数字:把对象转换为数字
对象 == 布尔:都转化为数字
字符串 == 数字:把字符串转换为数字
字符串 == 布尔:都转化为数字
布尔 == 数字:把布尔转换为数字
对象 == 字符串:把对象转换为字符串之后再比较
1==true //=> true
1==false //=> false
2==true //=> false,都转化为数字
[]==false //=> true,都转换为数字
[]==true //=> false,都转换为数字
![]==false //=> true,先算 ![] 为 false,再比较
![]==true //=> false,先算 ![] 为 false,再比较
[]==![] //=> true,先算 ![] 为 false,然后都转换为数字再比较