最近在重读《JavaScript高级程序设计》,读到数据类型这一节,想到了JavaScript里令程序员抓狂的一个问题——类型转换。由于JS是一门弱类型的语言,在运行时系统会根据须要进行类型转换,而类型转换的规则又使人迷惑。因而写篇博文尝试本身总结来加深印象。javascript
首先,咱们知道JavaScript里有7种数据类型:前端
boolean
number
null
string
symbol
undefined
object
java
object称为引用类型,其他的数据类型统称为“基本类型”。程序员
布尔值的强制类型转换使用的方法主要有: Boolean() 。其实布尔值的转换规则很好记住,由于转换后为false的值有限,只有下列几种:算法
null
undefined
false
+0
-0
NaN
""
数组
数字的强制类型转换使用的方法主要有:Number() parseInt() parseFloat() 和一元操做符。函数
Number() 函数的转换规则以下:学习
parseInt() 只处理字符串类型,若是接受的参数不是字符串类型,会先将其转换为字符串类型(稍后介绍字符串的强制转换)ui
parseInt() 函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直至找到第一个非空格字符。若是第一个字符不是数字字符或者负号,parseInt() 就会返回 NaN 。若是第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析完全部后续字符或者遇到了一个非数字字符。例:编码
var num1 = parseInt("123iuuan"); // 123(字母不是数字字符,被忽略)
var num2 = parseInt(""); // NaN
var num3 = parseInt("0xA"); // 10(十六进制数)
var num4 = parseInt(22.5); // 22)(小数点并非有效的数字字符)
复制代码
parseInt() 函数能够接收两个参数,第一个参数是需转换字符串,第二个参数是转换是使用的基数(即多少进制),例如:
var num1 = parseInt("AF", 16); //175
var num2 = parseInt("AF"); //NaN
复制代码
当指定基数时,字符串能够被成功转换,而第二个转换时,按以前说的转换规则,第一个字符不是数字字符,因此直接返回了NaN。
对于同一个字符串,若是指定的基数不一样,转换的结果也会受影响,例如:
var num1 = parseInt("10", 2); //2 (按二进制解析)
var num2 = parseInt("10", 8); //8 (按八进制解析)
var num3 = parseInt("10", 10); //10 (按十进制解析)
var num4 = parseInt("10", 16); //16 (按十六进制解析)
复制代码
综上所述,当不指定基数时,parseInt() 会自行决定如何解析输入的字符串,因此为了不错误的解析,使用 parseInt() 时都应该指定基数。
要把一个值转换为一个字符串有两种方式,第一种是使用 toString() 方法,除了null和undefined以外,其他的数据类型都有这个方法,它返回相应值的字符串表现。在调用数值的 toString() 方法时,能够传递一个参数:输出数值的基数。默认的输出值与指定基数10时的输出值相同。
var iuuan = true;
alert(iuuan.toString()); // 'true'
var num = 7;
alert(num.toString()); // '7'
alert(num.toString(2)); // '111'
alert(num.toString(10)); // '7'
复制代码
在不知道要转换的值是否是 null 或 undefined 的状况下,还可使用转型函数 String() ,这个函数可以将任何类型的值转换为字符串。
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;
alert(String(value1)); // "10"
alert(String(value2)); // "true"
alert(String(value3)); // "null"
alert(String(value4)); // "undefined"
复制代码
一、对象转换为布尔值时,根据上文所说的 Boolean() 假值可知,转换后全部的对象都为true;
二、对象转换为字符串:
var objtostring1 = {
//toString返回基本类型值
toString:function(){
return null
}
}
var objtostring2 = {
//toString方法返回不是基本类型值,valueOf返回基本类型值
toString:function(){
return {}
},
valueOf:function(){
return undefined
}
}
var objtostring3 = {
//toString方法返回不是基本类型值,valueOf返回的也不是基本类型值
toString:function(){
return {}
},
valueOf:function(){
return {}
}
}
String(objtostring1); //'null'
String(objtostring2); //'undefined'
String(objtostring3); //Uncaught TypeError: Cannot convert object to primitive value
复制代码
三、对象转换为数值:
var objtonum1 = {
//valueOf返回基本类型值
valueOf:function(){
return null
}
}
var objtonum2 = {
//valueOf方法返回不是基本类型值,toString返回基本类型值
valueOf:function(){
return {}
},
toString:function(){
return 1
}
}
var objtonum3 = {
//valueOf方法返回不是基本类型值,toString返回的也不是基本类型值
valueOf:function(){
return {}
},
toString:function(){
return {}
}
}
Number(objtonum1); //0 null转换为数值后为0
Number(objtonum2); //1
Number(objtonum3); //Uncaught TypeError: Cannot convert object to primitive value
复制代码
与显示类型转换使用函数方法不一样,隐式类型转换发生在是使用操做符或者语句中间。
当 + 操做符做为一元操做符时,对非数值进行 Number() 转型函数同样的转换;
var s1 = "01",s2 = "1.1",s3 = "z";,b = false,f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1 = +s1; // 值变成数值 1
s2 = +s2; // 值变成数值 1.1
s3 = +s3; // 值变成 NaN
b = +b; // 值变成数值 0
f = +f; // 值未变,仍然是 1.1
o = +o; // 值变成数值-1
复制代码
当 + 操做符做为加法运算符时,会应用以下规则:
var s1 = "01",s2 = "1.1",b = false,f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1 + s2 //'011.1'
s1 + b //'01false'
s2 + f //'1.11.1'
s1 + o //'01-1'
复制代码
当 - 操做符做为一元操做符时,对非数值进行 Number() 转型函数同样的转换以后再取负;
var s1 = "01",s2 = "1.1",s3 = "z";,b = false,f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1 = -s1; // 值变成了数值-1
s2 = -s2; // 值变成了数值-1.1
s3 = -s3; // 值变成了 NaN
b = -b; // 值变成了数值 0
f = -f; // 变成了-1.1
o = -o; // 值变成了数值 1
复制代码
当 - 操做符做为加法运算符时,会应用以下规则:
逻辑非操做符会将它的操做数转换为一个布尔值,而后再对其求反。因此使用两个逻辑非操做符,实际上会模拟 Boolean() 转型函数的行为。
这两个操做符产生的值不是必须为Boolean类型,产生的值始终未两个运算表达式的结果之一。
对于逻辑与 && 来讲,若是第一个操做数条件判断为 false 就返回该操做数的值,不然就返回第二个操做数的值。
对于逻辑或 || 来讲,若是第一个操做数条件判断为 true 就返回该操做数的值,不然就返回第二个操做数的值。
看个例子:
var a = 'hello',b = '';
a && b; // '' a是真值,因此返回b
b && a; // '' b是假值,因此直接返回b,不对a进行判断
a || b; // 'hello' a是真值,因此直接返回a
b || a; // 'hello' b是假值,因此返回a
复制代码
能够看得出来,两个操做符在执行时都有一个特色:当第一个操做数能决定操做结果时,则不会对第二个操做数进行判断,而且直接返回第一个操做数的值。这种操做又称为短路操做。
等操做符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边均可能被转换),最终的比较方式等同于全等操做符 === 的比较方式。
ECMAScript5文档中关于非严格相等的比较算法,列出了有11中状况,文中就不一一列出了,能够自行去文档查看学习:抽象相等比较算法
这里说明一下ToPrimitive操做,这个操做是ECMAScript运行时系统进行自动类型转换的一种抽象操做,用于将对象类型转换为基本类型,转换规则以下:
如此绕的一串规则,不如来看几个例子:
7 == '7' // true 字符串与数字比较时,字符串转数值后比较
1 == true // true 操做数中有布尔值,布尔值转数值后比较,true为1
7 == true // false 原理同上至关于 7 == 1
[] == 0 // true []先调用valueOf,返回值非基本类型,再调用toString,返回为'',空字符串转数值后为0
[] == [] // false 做为引用类型,内存地址不一样
复制代码
总结起来就是一下几条:
一样的,文档中的规则很是长,就不列出来了,抽象关系比较算法
//两边均为字符串
'7' > '20'; // true 按字符编码进行比较
//两边不全是字符串
7 > '20'; // false 字符串转为数值后进行比较
//两边全不是基本类型
[7] > [20]; // true 数组调用valueOf返回非基本类型,再调用toString方法返回字符串。
var obj = {},obj1 = {};
obj > obj1; // false
复制代码
总结起来,比较关系符的类型转换比较规则就是:
最后来讲说 obj >= obj1 的特殊现象
var obj = {},obj1 = {};
obj < obj1; // false
obj == obj1; // false
obj > obj1; // false
obj >= obj1; // true
obj <= obj1; // true
复制代码
前面三个结果不难理解,非严格相等判断时,均为空对象,但引用地址不一样,返回false。比较两个对象时,先进行ToPrimitive操做,均返回 ''[object Object]''
,因此不存在大小关系,也返回false。那为何a <= b和a >= b的结果返回的是true呢?
由于根据规范,a <= b 实际上执行的是 !(a > b),即咱们理解的<=是“小于或等于”,但JavaScript执行的是“不大于”的操做,因此 a > b 为false,那么 a <= b 天然为true了。
JavaScript做为一门弱类型语言,其中的类型转换规则总结起来真是让人头疼。固然,熟练掌握这些规则,并非为了在实际开发中写出这些晦涩代码,而是经过理解,使得咱们可以避免在代码编写的过程当中避免触碰到没必要要的类型转换,提升代码的稳定性和可维护性。
笔者做为前端菜鸟,文中若有错误,欢迎指出,共同交流、进步。
《JavaScript高级程序设计》