分享一道笔试题:
let x = { toString(){ return 20; }, valueOf(){ return '30'; }}console.log(x == '20');console.log(x == 30);复制代码
答案是false true 为什么?
让我们回到红宝书中,对于相等描述符是这样表述的:
==是相等操作符,如果两个操作数相等,则返回true,而不相等操作符由叹号后更等于号(!=)表示,如果两个操作数不相等,则返回true,这两个操作符都会先将操作数强制转换,再进行比较。 一般遵循下面规则:
-
如果一个操作数是字符串。另一个操作数是数值,在比较相等性之前先将字符串转换为数值。
-
如果一个操作数是对象,另外一个不是,则调用对象的valueOf()方法,用得到的基本类型来进行比较。
-
null和undefined是相等的。
-
要比较相等性之前,不能将null和undefined转换成其他任何值。
-
如果有一个操作数是NaN,相等操作符返回false,不相等操作符返回true。 回到题目,因为在两次比较中,x是对象另外一个都不是对象,就会调用对象的valueOf方法。所以就会变成
console.log(x.valueOf() == '20') //console.log(30 == '20')console.log(x.valueOf() == 30) //console.log('30' == 30)复制代码
之后就是字符串与数值比较,字符串先转换为数值,然后判断就可以了。
其实对于这道题,就延伸出了JS中一个令大家都感到头疼的类型转换!! 那我们就来看看吧!
原始值到原始值(数字,字符串,布尔值)的转换
-
原始值转化为布尔值,所有的假值("undefined","null",0,-0,NaN,"")都会被转化false,其他都会被转化为true.
-
原始值转换为字符串相当于加""
-
原始值转化为数字,布尔转文字:true --> 1,false --> 0 字符串转数字,只有字符串中都是以数字表示的,就可以直接转换为字符串,如果两个数字间有空格的话,那么转换结果就是NaN.
+"123" //123 +"1 3" // NaN复制代码
对象到原始值的转换
- 对象转换为布尔值都是true
- 对象到字符串
- 如果对象有toString()方法,就调用toString()方法,如果这个方法返回原始值,就将这个对象转化为字符串。
- 如果对象没有toString()方法或者该方法返回的不是原始值,那么就会调用该对象的valueOf()方法,如果存在就调用这个方法,如果返回值是字符串就转换为字符串。
- 否则就报错。
+运算符如何进行类型转换。
-
如果作为一元运算符就是转化为数字,常常用来将字符串转化为数字。
+“2” //2复制代码
-
如果作为二元运算符就有两种转换方式
- 两边如果其中一边有字符串,就会将另外一边转化为字符串进行拼接。
- 如果两边没有字符串,两边都会转化数字相加,对象也会根据前面的的方法转化为原始值数字。
- 如果其中的一个操作数是对象,则将对象转换为原始值,日期对象会通过toString(),其他对象会通过valueOf()方法进行转换,但是大多数对象都是不具备可用的valueOf()方法,所以还是会通过toString()方法执行转换。(也就是先用valueOf再用toString方法)
让我们来看看一道经典题目吧!
(! + [] + [] + ![]).length复制代码
运算顺序应该是这样的:
-
首先,![]会转换为"false", +[]会转换为0,此时式子变成
!0 + [] + false复制代码
-
!0 转换为 true,此时式子变成 true + [] + false
-
第三步中间的 []会转为空字符串,在
+ 运算符如何进行类型转化
第二条的第三点,对象会被转化为原始值,就是空字符,所以经过第三步之后就会变成"true" + false复制代码
-
"truefalse"复制代码
-
"truefalse".length = 9复制代码
可能你看到这里就有点疑问,为什么![]是false?
逻辑非运算符
简单的来说,就是首先把数据转化为布尔值,然后取反,结果为true或false。 根据规则: 原始值转化为布尔值,所有的假值("undefined","null",0,-0,NaN,"")都会被转化false,其他都会被转化为true. 所以![] = !true = false
toString和valueOf方法
上面不止一次的提到了toString和valueOf方法,那这到底是怎么计算的?
-
toString用来返回对象的字符串表示
var obj = {}; console.log(obj.toString());//[object Object] var arr2 = []; console.log(arr2.toString());//""空字符串 var date = new Date(); console.log(date.toString());//Sun Feb 28 2016 13:40:36 GMT+0800 (中国标准时间)复制代码
-
valueOf方法返回对象的原始值,可能是字符串、数值或bool值等,看具体的对象。
var obj = { name: "obj" }; console.log(obj.valueOf());//Object {name: "obj"} var arr1 = [1]; console.log(arr1.valueOf());//[1] var date = new Date(); console.log(date.valueOf());//1456638436303复制代码
如代码所示,三个不同的对象实例调用valueOf返回不同的数据
总结一波
-
undefined == null,结果是true。且它俩与所有其他值比较的结果都是false。
-
String == Boolean,需要两个操作数同时转为Number。
-
String/Boolean == Number,需要String/Boolean转为Number。
-
Object == Primitive,需要Object转为Primitive(具体通过valueOf和toString方法)。
写在最后
//大坑 console.log ( [] == 0 );//true console.log ( ! [] == 0 );//true //神坑 console.log ( [] == ! [] );//true console.log ( [] == [] );//false //史诗级坑 console.log({} == !{});//false console.log({} == {});//false复制代码
好好思考一下上面的坑吧!
参考资料:
https://segmentfault.com/a/1190000008432611 https://mp.weixin.qq.com/s/wd8JLGtnsoQYfm3K7KXO0g