# JS容易出现误区的运算符

## 相等运算符

### `==`

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

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

![](https://4236962461-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LI4XIh9ljNl7vLDogAQ%2F-LI4XSJWR6h8WVO802xk%2F-LI4XbJ1wGn6GtWXn7FI%2F%3D%3D.png?generation=1532322546284517\&alt=media)

### `Object.is(A,B)`

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

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

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

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

![===](https://4236962461-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LI4XIh9ljNl7vLDogAQ%2Fuploads%2Fgit-blob-9c0b5f1d90f3fa018767f0b217ffc59eff936469%2F%3D%3D%3D.png?alt=media)

### 隐式转换

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

下面单独聊一聊隐式转换

## 隐式转换`ToPrimitive()`

默认用法：

```js
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`对象求原值，则`PreferredType`是`String`，其他均为`Number`。
* `PreferredType`是`String`，则先调用`toString()`，结果不是原始值的话再调用`valueOf()`，还不是原始值的话则抛出错误；
* `PreferredType`是`Number`，则先调用`valueOf()`再调用`toString()`。
* 总结来讲就是：如果是对`Date`对象做隐式转化，先调用`toString`, 如果不是原始值的话，调用`valueOf()`，还不是原始值的话则抛出错误; 对其他对象做隐式转化的话，则先调用`valueOf()`再调用`toString()`。

## `+` `-` `++` `--`运算符

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

```js
+[]  //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'
*/
```

下列表达式输出什么

```js
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);
```

```js
'122'
'32'
'02'
'112'
'NaN2'
NaN
```

运算过程注意一下几点：

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

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

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

    ```js
    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在二进制中表示为

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

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

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

## 参考

* [JavaScript 中的相等性判断](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness)
* [js隐式装箱-ToPrimitive](https://sinaad.github.io/xfe/2016/04/15/ToPrimitive/)
