-
在对类型和值有全面的了解之后,讨论一个非常有争议的话题:强制类型转换。
-
关于强制类型转换是一个设计上的缺陷还是有用的特性,这一争论从JavaScript诞生就有了。
-
在很多书籍中强制转换被说成危险。晦涩和糟糕的设计。
一、值类型转换
- 将值从一种类型转换成另一种类型通常称为类型转换,这是显式的情况;隐式的情况称为强制类型转换。
- 强制类型转换主要将值转换为相应的字符串、数字、布尔值,不会返回对象和函数。
- 下面两种方式都是将数字转换成字符串。
var a = 42;
var b = a + ""; // 隐式强制类型转换
var c = String(a); // 显式强制类型转换
关于显隐式是作者个人的区分:简而言之,显式类型转换比较明显。
二、抽象值的操作
1. ToString
- 将引用类型数据转换为字符串,
- 使用工具函数
JSON.stringify()
在讲JSON
对象转为字符串时也使用了toString()
。
2. ToNumber
- 将非数字值转化为数字来使用。
- 比如:true转换为1,false转换为0
3. ToBoolean
- 我们常常误以为数值1和0分别等同于true和false,但其实不是这样。(其它语言可能是)
- 其实布尔值和数字是不一样的,虽然1可以强制转换为true。
假值
假值,可以转换为false的值。以下都是假值:
undefined
null
+0、-0和NaN
“”
假值对象
- 所有对象都是真值。假值对象不属于JavaScript语言的范畴。
- 浏览器在某些特定情况下,在常规JavaScript语法的基础上,自己创建了一些外来值,这些就是“假值对象”。
真值
真值就是假值之外的值,比如
var a = []; // 真值
三、显式强制类型转换
显示强制类型转换就是那些显而易见的类型转换。
-
字符串与数字
String()
、Number()
、toString()
- 加号(也可以认为是隐式)
-
解析数字字符串
Number()
、parseInt()
- 解析从左到右的顺序,如果遇到非数字字符就停止。
-
转换为布尔值
Boolean()
、!!
四、隐式强制类型转换
- 隐式强制类型转换,是指哪些隐蔽的强制类型转换,副作用不明显。
- 显式类型转换旨在让代码更加清晰可读,隐式强制类型转换会让代码变得晦涩难懂。
- 隐式强制类型转换的作用是减少冗余,让代码简洁。
- 所以就是在代码简洁和可读性之间进行取舍。
1. 字符串与数字之间
- +运算符
- 能用于数字计算,也能用于字符串拼接。
- 只要有一个为字符串就会拼接,实际更加复杂一些。
- 比如
a+“”
;这种将数字转换为字符串的方式非常常见。
- -运算符
- 强制转换为数字
- 例如:
a - 0
会将a强制转换为数字
2. 转换为布尔值
作为条件判断表达式的时候,会强制转换为布尔值。
if()
语句while()
循环a?b: c
三元表达式- 逻辑运算符
||
和&&
左边的操作数
3. ||和&&
- 与其他语言不同,在JavaScript中它们返回的不是布尔值。
- 它们返回的值是两个操作数中的一个。
||
,如果第一个操作数为true
,就返回第一个操作数的值,如果为false就返回第二个的值
&&
则相反。
function foo() {
console.log(a);
}
var a = 42;
a && foo();// 42
foo()
只有在条件判断a通过时才会被调用。
五、宽松相等和严格相等
宽松相等是:==
严格相等是:===
1. 相等比较操作的性能
- 常见误区:
==
检查值是否相等,===
检查值和类型是否相等。 - 正确解释:
==
允许在相等比较中进行强制类型转换,而===
不允许。 - 区别:第二种解释中
==
的工作量大一些,因为类型不同还要强制转换。(性能上也只是只有细微差别) - 理解
- 如果比较的两个值类型相同,则两者使用相同的算法。
==
和===
都会检查操作数的类型,区别在于操作数不同时它们的处理方式不同。
2. 抽象相等
ES5规范的11.9.3节的“抽象相等比较算法”定义了==运算符的行为。涵盖了它们进行强制类型转换的方式。
具体查看:
JavaScript 中的相等性判断 - JavaScript | MDN (mozilla.org)
这里讨论一些比较常见的规则。
- 如果两个值的类型相同,就仅比较它们是否相同。(42等于42)除了NaN不等于NaN,+0等于-0。
- 如果比较的是两个对象,两个对象指向同一个值时,即视为相等,不发生类型转换。
- 如果比较的是两个不同类型的值,会将其中之一或两者都转换为相同的类型后进行比较。
字符串与数字
- 宽松比较是字符串强制转换为数字进行比较
var a = 42;
var b = "42";
a === b; // false
a == b; // true
- b转换为数字是42,也就是两边42,相等。
其他类型与布尔值
- 举个字符串和布尔值的情况。这里是两边都转换为数字,再进行比较。
var a = "42";
var b = true;
a == b; // false
- “42”通过
ToNumber()
为42,true
转换为1,所以不相等。 - 建议不要使用
== true
和==flase
,因为可能对布尔值进行转换,产生意想不到的结果。
null和undefined
null == undefined; // true
- 这两个之间的强制转换是安全可靠的,可以利用
if(a === undefined || a === null) {
// ...
}
// 相当于
if(a == null){
// ...
}
对象与非对象
- 对象会调用
ToPrimitive
进行抽象操作
var a = 42;
var b = [ 42 ];
a == b; // true
- [ 42 ]先转换为“42”,再转换为42,对比二者相等。
比较少见的情况
假值的相等比较,存在24中情况,有些还无法理解,比如:
false == ""; // true
false == []; // true
false == {}; // false
...
极端情况
[] == ![] // true
这里是![]
变成false
,[] == false
结果为true
还有
2 == [2]; // true
"" == [null]; //true
3. 安全运用强制类型转换
- 如果两边的值中有
true
或者false
,千万不要使用==
- 如果两边的值中有
[]
、""
或者0
,尽量不要使用==
typeof
操作是绝对安全的。(typeof == “function”
和typeof === "function"
一样)==
和===
选择哪一个取决于是否允许再相等比较中发生强制类型转换。- 强制类型转换在很多地方非常有用,能够让相等比较更简洁,但是在部分情况下确实很危险。
4. 抽象关系比较
a < b
中存在强制转换,但是不太引人注意。- 比较双方调用
ToPrimitive
,如果结果出现非字符串,就转换为数字,进行数字比较。 - 如果双方都是字符串,或者调用
ToPrimitive
结果为字符串,就进行字符串比较。(字符串比较按字母顺序)