字符串和数字之间的隐式强制类型转换
先看一组例子
"42" + "0"; //420
42 + 0; //42
这种情况通常我们会理解为,+运算符的任意一边(或者两遍都)出现了字符串则是字符串拼接
那么再看一组例子
[1, 2] + [3, 4]; //1,23,4
事实上,正确的解读应该是
如果某个操作数是字符串或者能通过以下操作(从对象)转换为字符串的话,则进行拼接操作
- valueOf()
- toString()
(事实上是抽象操作ToPrimitive先通过内部操作[[DefaultValue]]检查是否有valueOf方法,如果有并返回基本类型值,就使用该值进行强制类型转换,如果没有就使用toString()的返回值(如果存在)来进行强制类型转换)
这里有个坑是关于{}+[]和[]+{}分别等于0和"[object Object]"的
{}+[]中{}被当做了一个独立的空代码块,不执行任何操作,因此实际上运行的是+[],+作为一元运算符将[]强制转换为数字0
而[]+{}中{}被当做一个值(空对象),因此被转换为""+"[object Object]",结果为"[object Object]"
在代码中我们经常可以看到+""的形式来进行隐式的强制类型转换
typeof(42 + "") //string
但是要注意一个和显式强制类型转换(String(..))细微的区别
隐式会先对两边的运算数调用valueOf(..)方法,而String(..)则是直接调用toString(..)方法,所以如果自己定义了valueOf(..)方法的返回值跟toString(..)返回值不一致,那么结果也会不一致
var a = {
valueOf: function() {
return 4;
},
toString: function() {
return 42;
}
}
console.log(
a + "", //4
String(a), //42
)
再来看看字符串转数字的情况
typeof("123" - 0); //number
因为只有数字才会进行-运算(*1,/1也一样,但是不常见),因此"123"字符串被隐式强制转换为了数字(先toString转换为字符串,然后再转换为数字)
布尔值到数字的隐式强制类型转换
布尔值转数字的情况比较少
考虑下面的情形
需要一个函数,当传入的参数中,有且仅有一个参数为true,则返回true
单独进行判定的话,如果有很多参数的时候会很麻烦,这时候可以使用从布尔值转换为数字(0,1)的强制类型转换
function onlyOne() {
var sum = 0;
for(let i = 0; i < arguments.length; i++) {
sum += !!arguments[i];
}
return sum == 1;
}
隐式强制类型转换为布尔值的情况
- if(..)
- for(..;..;..) 中第二个
- while(..)
- ?:三元运算符
- || , && 左侧的操作数
以上情况中,会被隐式强制类型转换为布尔值
||和&&
其他语言中的||和&&逻辑运算符返回的都是一个布尔值(true或false)
但是JavaScript中的行为有些不太一样
"a" || "b" //a
null || "b" //b
||会首先对第一个操作数进行条件判断(强制类型转换为布尔值)
如果第一个数为true就返回第一个数,如果第一个数为false就返回第二个数
a||b 大致相当于 a?a:b
"a" && "b" //b
null && "b" //null
&&也是首先会对第一个操作数进行条件判断(强制类型转换为布尔值)
如果第一个数为true就返回第二个数,如果第一个数为false就返回第一个数
a&&b 大致相当于 a?b:a
之所以说大致相当于,是因为当a是一个复杂的表达式(比如有副作用的函数调用)时,它有可能被执行两次
(这里我不太明白,具体什么样的情况才会调用两次呢?如果有知道的,希望能够在评论说下,谢谢)
&&有一种不常见的用法,但在压缩代码中很常用,叫做守护运算符
比如 check && fun()
当check为真的时候才会运行fun(),即前面的表达式为后面的表达式把关 等同于if(check){fun()}
Symbol的强制类型转换
Es6中的Symbol的强制类型转换有个坑
var sym = Symbol("cool");
String(sym), //Symbol(cool)
sym + "" //TypeError
可以看到,显示转换为字符串是可行的,但是隐式转换则会报错
而转换为数字则不管隐式还是显式都会报错
布尔值的话显示隐式都为true