在js中,类型转换是一个被非常多人诟病的地方。新手看了会发矇,老手看了会头疼。
类型转换,又成为强制类型转换,主要区分为显式强制类型转换和隐式强制类型转换
按我理解,类型转换的意思就很明显,就是当程序运行时需要此刻的变量的类型与变量的实际类型不符时,就会进行强制转换,在一些静态语言中,这个过程发生在编译阶段,或者干脆就抛出错误。而在js中,这个转换过程发生在运行时,所以你写代码的时候并不会意识到自己已经掉进坑里了。
而显式强制类型转换,简单的说,就是你觉得你可以明面上一眼看出来的,比如‘’ + number
,+string
,Boolean(value)
; 而隐式强制类型转换就是反过来的意思。
举个栗子:
var a = 'value';
if (a == true) {
console.log('a is true');
} else if (a == false) {
console.log('a is false');
}
按照正常的脑回路,一般人不会这么写。但是确实是有人会写if (a == true) {...}
这种语句,当然,后面的else if (a == false)
是为了节目效果加的。即便是这样,a == true
这种写法也是不可取的。
最后的输出是,两个都不输出。即在这个隐式强制类型转换中,a即不会等于true, 也不会等于false。
首先解释一下为什么a既不等于true, 也不等于false。按照正常人类的脑回路,应该是将a先转换为布尔值,然后再将两个布尔值对比。这种情况下,a转成布尔值只能转换成true或者false。要是这么想,那你就是too young, too simple了
当使用相等操作符==
进行判断时,将遵循以下规则(来自红宝书):
· 如果又一个操作数是布尔值,则在比较相等性之前先将其转换为数值(false转换为0,而true转换为1
· 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
· 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照之前的规则进行比较
· null和undefined是相等的
· 要比较相等性之前,不能将null和undefined转换成其他任何值
· 如果一个操作数是NaN,则相等操作符返回false。
· 如果两个操作数都是对象,则比较它们是不是同一个对象,是则返回true,否则返回false
所以就可以知道上例中,转换是如何进行的。首先两个相等操作的操作数中都包含布尔值,先将布尔值转换为数值所以这里true转换为1,false转换为0。接下来再次进行比较,此时两对操作数中都包含一个字符串操作数,则将字符串转换为数值, a转换为数值是NaN,此时再次进行比较,由于有一个操作数是NaN,按照规则,返回false。所以a既不等于true也不等于false。
由上可以看出,使用==
进行条件判断的时候是非常具有危险性的,尤其是当其中一个操作数为true的时候,如果另一个操作数不为布尔值,则很有可能就掉进坑里了。
那么一般在判断的时候怎么去进行判断比较好呢。我认为尽量不使用==
操作符。使用===
进行全等判断,或者直接将你需要判断的值丢进if判断里,比如if (a == true)
可以改成if (a)
或者if (!!a)
这样就能避开==
操作符的坑了。如果一定要使用==
进行比较,不用不舒服,则最好先将操作数进行显式类型转换为同一类型的数据,再进行比较,
知其然就要知其所以然。
为什么if (a)
和if (!!a)
就可以避开==
操作符的坑呢?它们进行强制类型转换时的转换规则又是怎样的。
其实在if的判断语句块内,如果判断值不是布尔值的话,会自动调用Boolean()函数进行布尔值的转换。而!!则相当于一次Boolean()的值类型转换了。所以在if的判断语句里直接放入判断条件和显式的将判断条件进行转换,其作用是一样的。区别也只是在于是否对后续看代码的人友好了,如果后面代码维护可能是接触代码不深的新手,则建议进行显式的转换。
由上面的解释可以知道if (a)
和if (!!a)
,if (Boolean(a))
的作用是一样的,所以这里只要了解一下Boolean()函数的转换规则就可以了:
任何非空字符串为true,空字符串('')为false
任何非零数字值(包括无穷大)为true,0和NaN为false
任何对象为true,null为false
undefined为false
以上规则来自红宝书,部分内容省略
看以上的Boolean()函数的转换规则,与我们日常工作中所需要的类型转换是非常契合的。
比如我们普遍会认为并要求if可以拦截空字符串,false,null,undefined,0与NaN
需要注意的是,虽然红宝书上并没明确指出,但三元运算符( ? :
)使用的转换规则也是Boolean()函数的转换规则.
而除了条件判断,在其他一些操作里也会出现类型转换,比如关系操作符(<, >, <=, >=
):
var a = '23';
var b = '3';
console.log(a < b); // true
上面这个转换可能很多的前端开发工程师都掉进过坑里并摸不着头脑。
可能你也看出来了出现‘23’
小于‘3’
的原因或许是在于它们都是字符串。如果关系操作符(<,>,<=,>=)两边都是字符串,则比较两个字符串对应的字符编码。'2'的字符编码是50,而3的字符编码是51,所以这里出现了‘23’小于‘3’的情况。
到这里很多人都会惊叹一声,并表示记住了此知识点。但是这样往往是不够的,你需要深挖下去,需要确切的了解关系操作符的转换规则,才能确保不会再次掉进它的坑里。
如果两个操作数都是数值,则执行数值比较 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值
如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较
如果一个操作数是对象,则调用这个对象的valueOf()方法,并用得到的结果根据前面的规则进行比较
如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较
如果一个操作数是NaN,undefined,则返回false
如果操作数为null, '', 则转换为0进行比较
多看看红宝书就会发现js中的类型转换其实坑还是很多的,还有一些加减乘除的操作符本身也会产生类型转换的问题。但是基本问题不是太大,所以就不单独拎出来了。
最后,一道思考题:
// do something ...
console.log(typeof a); // 'object'
console.log(a == false); // true
上面的对象为什么会等于false,这个题是之前刷到的一道面试题,可不是我瞎编的。。
最后,欢迎斧正,没有仔细的准备,所以写的少且杂,也是作为自己的一个总结回顾吧。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。