偶然看到几道题,顿时心情就不好了
[] + {};
{} + [];
[1,2] + [2,3];
[] == 0;
1 + + "22" + 3;
是不是很绝望,叱咤前端几年,居然被这几道题目给制裁了。如果上述题目都会做了,那么,请忽略本文,我说的都是废话。
现在开始吧。
[]+{}
[] + {}; //[object Object]
{} + []; //0
纳尼,换个顺序结果还变了。
首先普及下+作为二元运算符的作用:
如果某个操作数是字符串或者能够转换为字符串的话,+将进行拼接操作,否则执行数字相加。如果一个操作数是对象(包括数组),那么首先会调用valueOf,再调用toString()。
对普通对象来说,除非自行定义,否则toString()返回内部属性[[Class]]的值,如“[object object]”。数组因为有自己的toString()方法,它会直接进行调用。看个简单例子:
let a = {
toString(){
return 1;
}
}
a+1; //2
1+{c:1}; //1[object Object]
[1,2]+[2,3]; //"1,22,3"
对象a因为有toString(),所以他转化为字符串的时候,会调用它并返回1,所以a+1实际生效的是1+1。
而{c:1}没有定义toString,所以1+{c:1}会转化为1+“[object Object]”。数组的toString会用逗号将各元素拼接起来,[1,2]+[2,3]实际转化为"1,2"+“2,3”。
那么换个顺序为什么执行结果会变?
原因是{}出现在左侧,被当成了一个独立的代码块,实际生效的是+[],一元操作符+号是用来强制转化为数字,[]被转化为了0,所以{}+[]结果是0。
1+ +"22" + 3
细心的读者应该有发现,两个加号间有个空格。如果没有这个空格就会报错,它会被识别成自加符号。
但是现在留了空格,这个+号就是和"22"绑定在一起了,它的作用是强制转化为数字,所以实际生效的是
1+22+3 = 26。
一元操作符+号,可能会出现在这里
let timestamp = +new Date; // 1537257889834
它将日期对象强制类型转化为了数字,返回结果是unix时间戳。这样的代码似乎可以装一波逼。
[] == 0
==号的计算结果会根据两边的数据类型来进行处理。
1、数字x和字符串y
返回x == ToNumber(y)的值
1 == '12' //false
1 == '1' //true
‘12‘转化为数字为12,自然和1是不相等的
2、其他类型x和布尔类型y
返回x ==ToNumber(y)的值。
'12' == true
在没看这篇文章前,这题肯定有人会答true吧。true被转化为1,实际生效的是‘12’ == 1,所以为false。
if(a){} //正确用法
if(a == true){} //错误用法,原理如上,42为真值,但不会等于true
同理
0 == false //true
1 == true //true
3、null 和undefined
null == undefined 为true,且它俩与所有其他值比较的结果都是false。
4、对象x和非对象y
返回ToPrimitive(x) == y的值。
ToPrimitive(obj)等价于:先计算obj.valueOf(),如果结果为原始值,则返回此结果;否则,计算obj.toString(),如果结果是原始值,则返回此结果;否则,抛出异常。
其实上文的加号运算符已经有讲到。但是有一点要注意,你可以重写ToPrimitive。回到之前的例子
let a = {
toString(){
return 1;
},
[Symbol.toPrimitive]() {
return 2;
}
}
a + 2; //4
a == 2; //true
该方法在转化基本类型时优先级最高。
回到题目[] == 0,数组的valueOf是自身,然后再调用toString()得到'',所以实际生效的是''==0。''又转化为数字0,所以[] == 0。
最后出几道题目,看看大家有没有掌握。
null == 0;
[] == ![];
[12] == 12;