今天来谈谈JavaScript中的强化类型转换。
将值从一种类型转换为另一种类型通常称为类型转换
,这是显式情况,隐式的情况称为强制类型转换
。这里再将强制类型转换
区分为显式强制类型转换
和隐式强制类型转换
。当然这个区分是相对而言的。
抽象值操作
ToString
ToString
负责处理非字符串到字符串的强制类型转换。转换规则:null
转换为"null"
、undefined
转换为"undefined"
、true
转换为"true"
、数字的转换遵守通用规则,极大和极小值使用指数形式:1
2var a = 2.34 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000;
a.toString(); // "2.34e21"
对普通对象来说,如果有自己的toString()
方法,字符串化时会调用该方法并使用其返回值,否则Object.prototype.toString()
返回内部属性[[Class]]
的值,如"[Object Object]"
。
对数组来说,数组的默认toString()
方法经过的重新定义,将所有的单元字符串化后再用,
连接起来:1
2var a = [1,2,3];
a.toString(); // "1,2,3"
ToNumber
ToNumber
负责将数字值当作数字使用。转换规则:true
转换为1
、false
转换为0
、undefined
转换为NaN
、null
转换为0
。ToNumber
对字符串的处理基本遵守数字常量的规则,处理失败时返回NaN,不同之处是ToNumber
对以0开头的十六进制数并不按十六进制处理,而是按十进制。
对象(包括数组)会首先转换成相应的基本类型值,如果返回的是非数字类型的基本类型值,则再遵循以上规则将其强制转换为数字。这里依赖于ToPrimitive
操作,先检查该值是否有valueOf()
方法,如果有则返回基本类型值,再使用该值进行强制类型转换;如果没有就使用toString()
的返回值来进行强制类型转换。
如果valueOf()
和toString()
均不返回基本类型,会产生TypeError
错误。
注:使用Object.create(null)
创建的对象[[Prototype]]
属性为null
,并且没有valueOf()
和toString()
方法,因此无法进行强制类型转换。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21var a = {
valueOf: function(){
return "42";
}
};
var b = {
toString: function(){
return "42";
}
}
var c = [4,2];
c.toString = function(){
return this.join("");
}
Number(a); // 42
Number(b); // 42
Number(c); // 42
Number(""); // 0
Number([]); // 0
Number([1,2,3]); // NaN
ToBoolean
首先明确数字1和0跟布尔值true和false并不是一回事。JavaScript规定了一些可以被强制类型转换为false
的值:
- undefined
- null
- false
- +0、-0和NaN
- “”
以上称为假值
。假值
的布尔强制转换类型为false
。所有的对象都是真值,逻辑上讲假值列表以外的值都是真值
。1
2
3
4
5
6
7
8
9
10
11
12
13
14var a = new Boolean(false);
var b = new Number(0);
var c = new String("");
Boolean(a); // true
Boolean(b; // true
Boolean(c); // true
var d = [];
var e = {};
var f = function(){};
Boolean(d); // true
Boolean(e); // true
Boolean(f); // true
// []、{}、function(){}都不在假值列表中,所以他们都是真值
以上介绍了将值转换为string
、number
和boolean
的规则,接下来详细说明一下强制类型转换。
显式强制类型转换
字符串与数字之间的显式转换
字符串和数字之间是通过String()
和Number()
这两个内建函数来是实现的,他们前面没有new
关键字,并不创建对象。1
2
3
4
5
6
7var a = 42;
var b = String(a);
var c = '3.14';
var d = Number(c);
b; // '42'
d; // 3.14
除了String()
和Number()
以外,还有其他方法可以实现转换:1
2
3
4
5
6
7
8var a = 42;
var b = a.toString();
var c = 3.14;
var d = +c;
b; // '42';
d; // 3.14
a.toString()
是显式的,不过其中涉及隐式转换,因为toString()
对42这样的基本类型不适用,所以js会自动为42创建一个封装对象,然后对该对象调用toString()
;
+运算符显式的将c转换为数字,一元运算+被普遍认为是显式强制类型转换。
+运算符的另外一个用途是将日期对象强制转换为数字,返回时间戳:1
2
3
4var d = new Date("Tues, 27 Feb 2018 15:30:45 CDT");
+d; // 1519763445000
var timestamp = +new Date(); //常用来获取当前的时间戳
最好使用ES5中新加入的静态方法Date.now()
:1
var timestamp = Date.now();
注:应该使用Date.now()
来获取当前的时间戳,用new Date(...).getTime()
来获取指定时间的时间戳。
显式解析数字字符串
解析
字符串中的数字和将字符串强制类型转换
为数字返回的都是数字,但解析
和转换
之间还是有明显的区别:1
2
3
4
5
6
7var a = "42";
var b = "42px";
Number(a); // 42
parseInt(a); // 42
Number(b); // NaN
parseInt(b); // 42
解析
允许字符串中含有非数字字符,解析按从左到右的顺序,遇到非数字字符就停止;而转换
不允许出现非数字字符,否则会失败返回NaN
。
注:解析字符串中的浮点数可以使用parseFloat()
函数。parseInt()
和parseFloat()
都是针对字符串值,应避免向其传递非字符串值。
显式转换为布尔值
与String(..)
和Number(..)
一样,Boolean(..)
是显式的ToBoolean强制类型转换:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var a = '0';
var b = [];
var c = {};
var d = '';
var e = 0;
var f = null;
var g;
Boolean(a); // true
Boolean(b); // true
Boolean(c); // true
Boolean(d); // false
Boolean(e); // false
Boolean(f); // false
Boolean(g); // false
//参考上面的假值表
虽然Boolean(..)
是显式的,但不常用。与+类型,一元运算符!显式的将值强制类型转换为布尔值,但它同时还将真值转换为假值,所以显式强制类型转换为布尔值的最常用方法是!!,第二个!将结果反转:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var a = '0';
var b = [];
var c = {};
var d = '';
var e = 0;
var f = null;
var g;
!!a; // true
!!b; // true
!!c; // true
!!d; // false
!!e; // false
!!f; // false
!!g; // false
建议使用Boolean(a)
和!!a
来进行显式强制类型转换。
隐式强制类型转换
隐式强制类型转换的作用是减少冗余,让代码简洁。
字符串和数字之间的隐式强制类型转换
+
运算符既能用于数字加法,也能用于字符串拼接,如果某个操作数是字符串或者能够通过以下步骤转为字符串,+
将进行拼接操作。即如果某个操作数是对象(包括数组),则对其调用ToPrimitive
抽象操作,得到字符串,进行拼接操作。1
2
3
4var a = [1,2];
var b = [3,4];
a + b; // '1,23,4'
数组的valueOf()
操作无法得到简单的基本类型,于是接着调用toString()
操作,使用,
连接各元素,返回字符串。
总结: 如果+
的其中一个操作数是字符串(或者通过以上步骤能转为字符串),则执行字符串拼接操作,否则执行数字加法。
我们可以将数字和空字符串””相+
来将其转换为字符串:1
2
3var a = 42;
var b = a + '';
b; // "42"
a + ''
会对a调用valueOf()
方法,然后通过ToString
抽象操作将返回值转换为字符串,而String(a)
则是直接调用ToString()
。
它们最后都返回字符串,但a如果是对象的话结果可能不一样:1
2
3
4
5
6
7var a = {
valueOf: function() { return 42; }
toString: function() { return 4; }
}
a + ""; // "42"
String(a); // "4"
一般不会遇到上面的情况,但是在定制valueOf()
和ToString()
的时候要特别小心。
-
、*
、 /
运算符都只适用于数字,a-0
会将a
强制转换为数字。1
2
3
4var a = [3];
var b = [1];
a - b; // 2
为了执行减法运算,a和b都需要转换为数字,首先被转换为字符串(通过toString()),然后再转为数字。
b = String(a) (显式)和 b = a + “” (隐式)各有优点,b = a + “” 更常见一些。
隐式强制类型转换为布尔值
下面的情况会发生布尔值隐式强制类型转换:
- if(..)语句中的条件判断表达式。
- for(..; ..; ..;)语句中的条件判断表达式(第二个)。
- while(..)和 do..while(..)循环中的条件判断表达式。
- ? : 三元运算中的条件判断表达式。
- 逻辑运算符 || 和 && 左边的操作数(作为条件判断表达式)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var a = '42';
var b = 'abc';
var c;
var d = null;
if(a){
console.log('yep'); // yep
}
while(c){
console.log('nope, never runs');
}
c = d ? a : b;
c; // 'abc'
if((a && b) || c){
console.log('yep'); // yep
}