JS容易出现误区的运算符

相等运算符

==

==在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边都可能被转换),最终的比较方式等同于全等操作符===的比较方式。

记住下面这张表即可,特别注意的是Boolean值和String值被比较时,会ToNumber转换, Object会根据情况做隐式转换。

Object.is(A,B)

Object.is()主要是和===NaN+0-0 上面有区别,其他场景一样。

Object.is()判断NaN是相等的,+0-0是不相等的,刚好和 ===相反

Object.is(-0 , +0)  // false
-0 === +0  // true

Object.is(NaN , NaN)  // true
NaN === NaN  // false

隐式转换

[1,2] == '1,2' // true

下面单独聊一聊隐式转换

隐式转换ToPrimitive()

默认用法:

ToPrimitive(input [,PreferredType]),(ToPrimitive方法可被修改)

如果input是基本数据类型,则不转化,如果是object, 则按照下面方式转化

  1. 如果没有传入PreferredType参数,则让hint的值为'default'

  2. 否则,如果PreferredType值为String,则让hint的值为'string'

  3. 否则,如果PreferredType值为Number,则让hint的值为'number'

  4. 如果input对象有@@toPrimitive方法,则让exoticToPrim的值为这个方法,否则让exoticToPrim的值为undefined

  5. 如果exoticToPrim的值不为undefined,则

    1. result的值为调用exoticToPrim后得到的值

    2. 如果result是原值,则返回

    3. 抛出TypeError错误

  6. 否则,如果hint的值为'default',则把hint的值重新赋为'number'

  7. 返回OrdinaryToPrimitive(input,hint)

OrdinaryToPrimitive(input,hint)

input的数据类型是对象,hint的数据类型是字符串,并且hint的值要么是"string",要么是"number"。该抽象操作的步骤如下:

  1. 如果hint的值为'string',则

    1. 调用input对象的toString()方法,如果值是原值则返回

    2. 否则,调用input对象的valueOf()方法,如果值是原值则返回

    3. 否则,抛出TypeError错误

  2. 如果hint的值为'number',则

    1. 调用input对象的valueOf()方法,如果值是原值则返回

    2. 否则,调用input对象的toString()方法,如果值是原值则返回

    3. 否则,抛出TypeError错误

稍微总结一下:在没有改写或自定义@@toPrimitive方法的条件下,

  • 如果是Date对象求原值,则PreferredTypeString,其他均为Number

  • PreferredTypeString,则先调用toString(),结果不是原始值的话再调用valueOf(),还不是原始值的话则抛出错误;

  • PreferredTypeNumber,则先调用valueOf()再调用toString()

  • 总结来讲就是:如果是对Date对象做隐式转化,先调用toString, 如果不是原始值的话,调用valueOf(),还不是原始值的话则抛出错误; 对其他对象做隐式转化的话,则先调用valueOf()再调用toString()

+ - ++ --运算符

在加法的过程中,首先把等号左右两边进行了求原值ToPrimitive()操作,然后如果两个原值只要有一个是String类型,就把两个原值都进行转化字符串ToString()操作,进行字符串拼接;否则把两个原值都进行转化数字ToNumber()操作,进行数字相加。

+[]  //0
+[0] //0
+{}  //NaN

[] + []  // ""
// PreferredType是Number,则先调用valueOf()再调用toString()。 []==>[]==>""

{} + {}  //"[object Object][object Object]"
//{} PreferredType是Number,则先调用valueOf()再调用toString() {}==>{}==>"[object Object]"

[] + {}  //  "[object Object]"
//[]==>[] ==>""
//{} PreferredType是Number,则先调用valueOf()再调用toString() {}==>{}==>"[object Object]"
// 然后字符串拼接

{} + []  // 0
// 原因在于{}在前,被当成{};直接执行了,变成了 +[],[]的原值是"",+[]需要进行toNumber转换变成了0 ,最后结果为0

++[[ ]][+[ ]]+[+[ ]]
//这个就有意思了,一开始对[][]理解错了,以为是二维数组,二维数组是name[i][j],这个才是对二维数组的调用,[][]是对前面那个一维数组的调用,数组名就是第一个[].

/*
拆分:
>> ++[[]][+[]]   +  [+[]]  很明显 我们知道+[]的结果是0
>> ++[[]][0]  +   [0]   这里我们就看到是对数组[[]]的第0位进行操作,即[]
>> ++[]    +  [0]
>> ([]+1)  +  [0]
>> (''+1)  +  [0]
>> '1'     +  [0]
>>'10'
*/

下列表达式输出什么

console.log(1 + "2" + "2");
console.log(1 + +"2" + "2");
console.log(1 + -"1" + "2");
console.log(+"1" + "1" + "2");
console.log( "A" - "B" + "2");
console.log( "A" - "B" + 2);
'122'
'32'
'02'
'112'
'NaN2'
NaN

运算过程注意一下几点:

  • 多个数字和数字字符串混合运算时,跟操作数的位置有关

    console.log(2 + 1 + '3'); // '33'
    console.log('3' + 2 + 1); //'321'
  • 数字字符串之前存在数字中的正负号(+/-)时,会被转换成数字,同样,可以在数字前添加'',将数字转为字符串

    console.log(typeof '3'); // string
    console.log(typeof +'3'); //number
    console.log(typeof 3); // number
    console.log(typeof (''+3)); //string
  • 对于运算结果不能转换成数字的,将返回 NaN

    consol.log('a' * 'sd'); //NaN
    console.log('A' - 'B'); // NaN

&&||

需要注意的是返回结果

  • A && B 与运算,会做boolean转换,当A为false时返回A的结果,当A为true时返回B的结果,

  • A || B 或运算,当A为true时返回A的结果,当A为false时返回B的结果。

为什么0.1+ 0.2 != 0.3

在JS中,采用的是双精度版本(64位),十进制的数都是使用二进制表示的,0.1在二进制中表示为

0.1 = 2^-4 * 1.10011(0011)  // (0011) 表示循环

所以0.1表示的二进制与0.2表示的二进制相加,在转化为十进制就变成了0.30000000000000004

解决方案:parseFloat((0.1 + 0.2).toFixed(10))

参考

Last updated