在介绍强制转换之前,不论是显式的还是隐式的,我们需要了解字符串、数字和布尔值之间类型转换的基本规则,这些规则(也叫抽象操作)是仅供内部使用的。
toString
抽象操作toString
负责处理非字符串到字符串的强制类型转换。基本类型值得字符串化规则是:null
转换成"null"
,undefined
转换为"undefined"
,true
转换成"true"
。数字的字符串化则遵循通用规则。
toNumber
抽象操作toNumber
处理非数字值到数字值的强制类型转换。规则如下:
true
变成1
,false
变成0
,undefined
转换成NaN
,null
变成0
- 对字符串的处理基本遵循数字常量的相关规则和语法,处理失败返回
NaN
。 - 对象会首先转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
为了将值转换为相应的基本类型值,抽象操作ToPrimitive
首先通过内部操作DefaultValue
检查改制是否有valueOf()
方法,如果有并且返回基本类型值,就使用该值进行强制类型转换。如果灭有就使用toString()
的返回值来进行强制类型转换。如果两者都没有,产生TypeError
错误。
to Boolean
假值,即
undefined
null
false
+0
、-0
和NaN
""
都将强制转换为false
。
就算是封装了价值的封装对象,如:
var a = new Boolean(false),
b = new Number(0),
c = new String("");
a
、b
、c
的布尔强制转换都是true
,因为所有的对象都是真值。
上述三个操作包括toPrimitive
就是JavaScript处理类型转换的规则,有了他们,就可以更好地了解强制类型转换。
显式强制类型转换
字符串与数字之间的显式转换
字符串和数字之间的转换是通过String()
和Number()
这两个内建函数,即原生函数来实现的。
var a = 42,
b = String(a),
c = "3.14",
d = Number(c);
console.log(b); //"42"
console.log(d); //3.14
注意,它们前面没有
new
关键字,并不是创建封装对象。但var e = Array(1,2,3)
这个语句是构造一个数组对象,new
会被自动添加。
String()
遵循前面讲的toString
规则,Number()
遵循前面的toNumber
规则。
除了上面的方法外,还有方法可以实现两者的显式转换。
var a = 42,
b = a.toString(),
c = "3.14",
d = +c;
a.toString()
是显式的,不过其中涉及隐式转换。因为toString()
对象42
这种基本类型值不适用,所以会自动为42
创建一个封装对象,从而调用toString()
方法。值得注意的是,我们没有toNumber()
方法。对于d = +c
是通过一元运算符显式地将c
转换为数字,而非数字加法运算,也不是字符串拼接。至于说这是隐式还是显式,取决于个人理解。如果你知道这是一元运算符的形式,那就是显式。如果你不知道,你说是隐式也没有关系。但在开源社区里,一元运算符+
普遍认为是显式。
日期显式转换为数字
一元运算符的另外一个用途是将日期Date
对象强制类型转换为数字,返回Unix
时间戳,以微秒为单位(即从1970年1月1日00:00:00 UTC到当前时间):
var timestamp = +new Date();
当然,对于提取时间戳我们有更好的方法。
var timestamp = new Date().getTime();
或者使用ES5
中新加入的静态方法Date.now()
来获取当前的时间戳。
var timestamp = Date.now();
显式解析数字字符串
使用parseInt()
方法将字符串解析为整数,parseFloat()
方法将字符串解析为浮点数。这和将字符串强制类型转换为数字类似但不同的地方在于,解析允许字符串中含有非数字字符。
var a = "42px";
Number(a); //NaN
parseInt(a); //42
显式转换为布尔值
与前面的String()
和Number()
一样,Boolean()
是显式的toBoolean
强制类型转换。
虽然Boolean()
是显式的,但并不常用。大家都喜欢使用一元运算符!
将值强制转换为布尔值,但同时它也会将假值变为真值,将真值变为假值,所以,你可以使用!!
来做判断。
var a = {},
b = [];
!!a; //true
!!b; //true
在if()
语句中,如果没有使用Boolean()
和!!
,就会自动隐式进行toBoolean
转换。
隐式强制类型转换
字符串和数字之间的隐式类型转换
+
运算符既能用于数字加法,用能用于字符串拼接。如果+
的其中一个操作数是字符串(或者通过一定步骤得到的字符串),则执行字符串拼接操作。
var a = "42",
b = 0,
c = [1,2],
d = [3,4];
a + b; //"420"
c + d; //"1,23,4"
"1,23,4"
是怎么来的呢,这就是通过一定步骤得到的字符串。在ES5
规则中,如果其中一个操作数是对象(当然包括数组),则首先对其调用toPrimitive
抽象操作,该抽象操作再调用[[DefaultValue]]
。因为数组的ValueOf()
操作无法得到简单的基本类型值,于是它转而调用toString()
,因此两个数组变成了"1,2"
和"3,4"
。+
将她们拼接后返回1,23,4
。
依上,可以将数字和空字符串""
和+
来将其转换为字符串。
var a = 42,
b = a + "";
b; //"42"
a + ""
与显式的String(a)
的区别在于,a + ""
会对a
调用valueOf()
方法,然后通过toString
操作将返回值转换为字符串,而String()
则直接调用toString
抽象操作。所以,在定制valueOf()
和toString()
方法时就需要特别小心。
因为-
运算符是不会用于字符串操作的,所以可使用-
将字符串强制类型转换为数字。
var a = "3.14",
b = a - 0;
b; //3.14
隐式强制类型转换为布尔值
if()
语句的条件判断表达式for(..;..;..)
语句中的条件判断表达式while()
和do...while()
循环中的条件判断表达式?:
中的条件判断表达式||
、&&
左边的操作数
这里特地讲一下||
和&&
,这与其他语言(C
、PHP
)中的完全不同。在JavaScript
返回值是两个操作数其中的一个。
var a = 42,
b = "abc",
c = null;
a || b; //42
c || b; //"abc"
a && b; //"abc"
c && b; //null
||
和&&
首先会对第一个操作数执行条件判断,如果其不是布尔值就先进行toBoolean
强制类型转换,再执行条件判断。
-
对
||
来说,如果条件判断结果为true
就返回第一个操作数的值,如果为false
就返回第二个操作数的值。 -
对
&&
来说,刚好相反。判断条件为true
就返回第二个操作数的值,如果为false
就返回第一个操作数的值。
所以,基于上述原理,我们有了一种设置默认值的方法,很是方便。
function foo(a,b){
a = a || "hello";
b = b || "world";
console.log(a + " " + b);
}
foo (); // "hello world"
foo ("yeah", "yeah!"); //"yeah yeah!"
&&
常用语JavaScript
代码压缩工具。
function foo(){
console.log(a);
}
var a = 42;
a && foo();
foo()
只有在条件判断a
通过是才会被调用,如果为通过,a && foo()
就会短路,foo()
不会被调用。开发人员通常会使用if (a){foo();}
这种写法,但压缩工具通常使用a && foo()
,因为更简洁。
== vs ===
宽松相等==
和严格相等===
用来判断两个值是否相等。
正确的解释是:==
允许在相等比较中进行强制类型转换,而===
不可以。
有些人会认为说
==
检查值是否相等,===
检查值和类型是否相等。这是不正确的理解。
ES5
规范中使用抽象相等比较算法定义了==
运算符的行为。
- 如果两个值的类型相同,仅比较他们是否相等
- 两个对象指向同一值时,即视为相等,不发生强制类型转换
- 两个不同类型的值是会发生隐式强制类型转换
特殊例子有:
NaN
不等于NaN
,+0
不等于-0
。
字符串和数字之间的相等比较
ES5
规范11.9.3.4-5这样定义:
- 如果
Type(x)
是数字,Type(y)
是字符串,则返回x == toNumber(y)
的结果 - 如果
Type(x)
是字符串,Type(y)
是数字,则返回toNumber(x) == y
的结果
其他类型和布尔类型之间的相等比较
ES5
规范11.9.3.6-7这样定义:
- 如果
Type(x)
是布尔类型,则返回toNumber(x) == y
的结果 - 如果
Type(y)
是布尔类型,则返回x == toNumber(y)
的结果
null和undefined之间的相等比较
ES5
规范11.9.3.2-3这样定义:
- 如果
x
为null
,y
为undefined
,则结果为true
- 如果
x
为undefined
,y
为null
,则结果为true
对象与非对象之间的相等比较
ES5
规范11.9.3.8-9这样定义:
- 如果
Type(x)
是数字或字符串,Type(y)
是对象,则返回x == toPrimitive(y)
的结果 - 如果
Type(x)
是对象,Type(y)
是数字或字符串,则返回toPrimitive(x) == y
的结果
编辑备注:
- 2017-04-17第一次编辑