说说JavaScript的类型转换
栏目: JavaScript · 发布时间: 5年前
内容简介:最近在重读《JavaScript高级程序设计》,读到数据类型这一节,想到了JavaScript里令程序员抓狂的一个问题——类型转换。因为JS是一门弱类型的语言,在运行时系统会根据需要进行类型转换,而类型转换的规则又令人迷惑。于是写篇博文尝试自己总结来加深印象。首先,我们知道JavaScript里有7种数据类型:
最近在重读《JavaScript高级程序设计》,读到数据类型这一节,想到了JavaScript里令 程序员 抓狂的一个问题——类型转换。因为JS是一门弱类型的语言,在运行时系统会根据需要进行类型转换,而类型转换的规则又令人迷惑。于是写篇博文尝试自己总结来加深印象。
基本概念
首先,我们知道JavaScript里有7种数据类型:
boolean
number
null
string
symbol
undefined
object
object称为引用类型,其余的数据类型统称为“基本类型”。
显示强制类型转换
转换为Boolean类型
布尔值的强制类型转换使用的方法主要有: Boolean() 。其实布尔值的转换规则很好记住,因为转换后为false的值有限,只有下列几种:
null
undefined
false
+0
-0
NaN
""
转换为Number类型
数字的强制类型转换使用的方法主要有:Number() parseInt() parseFloat() 和一元操作符。
Number() 函数的转换规则如下:
- 如果是 Boolean 值, true 和 false 将分别被转换为 1 和 0。
- 如果是数字值,只是简单的传入和返回。
- 如果是 null 值,返回 0。
- 如果是 undefined ,返回 NaN 。
- 如果是字符串,遵循下列规则:
- 如果字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即 "1"会变成 1, "123" 会变成 123,而 "011" 会变成 11(注意:前导的零被忽略了);
- 如果字符串中包含有效的浮点格式,如 "1.1" ,则将其转换为对应的浮点数值(同样,也会忽略前导零);
- 如果字符串中包含有效的十六进制格式,例如 "0xf" ,则将其转换为相同大小的十进制整数值;
- 如果字符串是空的(不包含任何字符),则将其转换为 0;
- 如果字符串中包含除上述格式之外的字符,则将其转换为 NaN 。
细说parseInt
parseInt() 只处理字符串类型,如果接受的参数不是字符串类型,会先将其转换为字符串类型(稍后介绍字符串的强制转换)
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() 时都应该指定基数。
转换为String类型
要把一个值转换为一个字符串有两种方式,第一种是使用 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() ,这个函数能够将任何类型的值转换为字符串。
- 当值有 toString() 方法是,调用该方法并返回结果;
- 值是null时,返回"null";
- 值是undefined时,返回"undefined"。
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" 复制代码
对象转换为基本类型
1、对象转换为布尔值时,根据上文所说的 Boolean() 假值可知,转换后所有的对象都为true;
2、对象转换为字符串:
- 判断对象是否有 toString() 方法,如果有 toString() 方法且返回的结果是基本类型值,就返回这个结果并转换为字符串;
- 如果对象没有 toString 方法或者该方法返回的不是原始值,就判断该对象是否有 valueOf 方法。如果存在 valueOf 方法且返回值是基本类型值,就返回并转换为字符串;
- 否则就抛出错误。
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 复制代码
3、对象转换为数值:
- 对象转换为数值的操作与转换为字符串基本相似,只是转换时先调用 valueOf ,不存在或返回值不是基本类型值时,再调用 toString 方法。
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 复制代码
当 + 操作符作为加法运算符时,会应用如下规则:
- 如果两个操作数都是字符串,则进行简单的字符串拼接;
- 如果只有一个操作数是字符串,则将另一个转换为字符串再进行拼接,转换为字符串的操作与显示转换时规则相同;
- 如果有一个操作数是对象、数值或布尔值,则调用它们的 toString 方法取得相应的字符串值,然后再应用前面关于字符串的规则
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运行时系统进行自动类型转换的一种抽象操作,用于将对象类型转换为基本类型,转换规则如下:
- 检查该值是否有 valueOf 方法。如果有且返回基本类型值,则使用该值;
- 如果没有就使用 toString 方法的返回值(如果存在)来进行强制类型转换;
- 如果 valueOf 或者 toString 都不返回基本类型值,则会报错 TypeError。
如此绕的一串规则,不如来看几个例子:
7 == '7' // true 字符串与数字比较时,字符串转数值后比较 1 == true // true 操作数中有布尔值,布尔值转数值后比较,true为1 7 == true // false 原理同上相当于 7 == 1 [] == 0 // true []先调用valueOf,返回值非基本类型,再调用toString,返回为'',空字符串转数值后为0 [] == [] // false 作为引用类型,内存地址不同 复制代码
总结起来就是一下几条:
- null和undefined互相比较时,结果为true,其余任何类型与这两个值比较都为false;
- 操作数中存在数值,则将另一个操作数转换为数值再比较;
- 操作数中没有数值但有字符串,则将另一个操作数转换为字符串再比较;
- 操作数中的布尔值都转换为数值。非基本类型都先进行ToPrimitive操作,按上述三条顺序进行比较。
比较关系符
同样的,文档中的规则非常长,就不列出来了,抽象关系比较算法
//两边均为字符串 '7' > '20'; // true 按字符编码进行比较 //两边不全是字符串 7 > '20'; // false 字符串转为数值后进行比较 //两边全不是基本类型 [7] > [20]; // true 数组调用valueOf返回非基本类型,再调用toString方法返回字符串。 var obj = {},obj1 = {}; obj > obj1; // false 复制代码
总结起来,比较关系符的类型转换比较规则就是:
- 如果操作数中存在非基本类型,先进行ToPrimitive操作;
- ToPrimitive操作转换后如果操作数出现数值,那么将操作数转换为数值进行比较;
- ToPrimitive操作转换后如果操作数均为字符串,那么按照字符编码值进行比较。
最后来说说 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的类型转换》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。