> For the complete documentation index, see [llms.txt](https://books.innohub.top/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://books.innohub.top/webinfo/js-operators.md).

# JS 部分运算符操作

\[TOC]

## 1. 关于对象的运算符操作

如果运算子是对象可以定义对象的`valueOf` 属性为函数，实际运算中通过调用该函数获取对象的实际值，在实际调用是`obj.valueOf().toString()` 返回一个对象的值。

```javascript
> obj.valueOf = () => {return 12;}
[Function]
> obj
{ valueOf: [Function] }
> obj.valueOf()
12
> obj + 1
13
> obj > 1
true
> obj === '12'
false
> obj === 12
false
> obj == 12
true
> obj + 'test'
'12test'
> '' + obj
'12'
> '0' + obj
'012'
```

## 2. 严格相等比较

俗语严格相等比较时，如果两个值的类型不同，直接返回false，不会进行类型转化；对于同一类型的原始类型的值比较，相等则返回true；对于复合类型的数据进行比较，不是比较是否相等而是比较是否为同一地址，**注意对于对象的比较，严格比较运算符比较的是地址，大于，小于比较的是对象的数值** **对于三个特殊值：undefined null 与自身严格相等 NaN 与任何值严格比较均不等**

```javascript
> 1 === 0x1
true
> NaN === NaN
false
> [] === []
false
> {} === {}
false
```

## 3. 二进制运算符

JS中一共有7个二进制位运算符，直接处理每一个bit。需要注意的是位运算符只对整数起作用，虽然JS中所有数字都是以64位浮点数进行存储的，但是进行位运算时，是用32位带符号位的整数进行运算的，返回值也是一个32位的带符号整数。使用位运算符会有较好的性能。

可以使用`|` 运算符将任意数转换为32位整数,使用二进制运算符处理小数时，会将小数部分舍去，只处理整数部分

```javascript
function toInt(a) {
    return a | 0;
}
toInt32(1.001) // 1
toInt32(1.999) // 1
toInt32(1) // 1
toInt32(-1) // -1
toInt32(Math.pow(2, 32) + 1) // 1
toInt32(Math.pow(2, 32) - 1) // -1

0 & 12  // 0
```

**否运算符～：**

否运算符会将所有32位整数的每一位都取反，注意数字是以补码表示，所以对于3取反后`00000000000000000000011` 变为 `111111111111111111111100` ,补码表示的-4，取反值与原值相加为-1

对于非数字类型进行取反，会默认调用`Number()` 函数，先转化为数字,对于NaN null undefined 取反均为-1

```javascript
> ~~109
109
> ~47.12
-48
> ~'011'
-12
> ~'0110'
-111


> ~'12 va'
-1
> Number('12 vds')
NaN
> ~[]
-1
> ~NaN
-1
> ~null
-1
```

**带符号位的右移**

js支持基本的左移运算以及右移运算,也可以进行带符号位的右移,即进行右移操作时符号位也参与移动,头部补0,所以进行该运算时得到的额结果一定为正

**位运算符可以用作设置对象属性的开关**

假定某个对象有四个开关，每个开关都是一个变量。那么，可以设置一个四位的二进制数，它的每个位对应一个开关。

```
var FLAG_A = 1; // 0001
var FLAG_B = 2; // 0010
var FLAG_C = 4; // 0100
var FLAG_D = 8; // 1000
```

上面代码设置 A、B、C、D 四个开关，每个开关分别占有一个二进制位。

然后，就可以用二进制与运算检验，当前设置是否打开了指定开关。

```
var flags = 5; // 二进制的0101

if (flags & FLAG_C) {
  // ...
}
// 0101 & 0100 => 0100 => true
```

上面代码检验是否打开了开关`C`。如果打开，会返回`true`，否则返回`false`。

现在假设需要打开`A`、`B`、`D`三个开关，我们可以构造一个掩码变量。

```
var mask = FLAG_A | FLAG_B | FLAG_D;
// 0001 | 0010 | 1000 => 1011
```

上面代码对`A`、`B`、`D`三个变量进行二进制或运算，得到掩码值为二进制的`1011`。

有了掩码，二进制或运算可以确保打开指定的开关。

```
flags = flags | mask;
```

二进制与运算可以将当前设置中凡是与开关设置不一样的项，全部关闭。

```
flags = flags & mask;
```

异或运算可以切换（toggle）当前设置，即第一次执行可以得到当前设置的相反值，再执行一次又得到原来的值。

```
flags = flags ^ mask;
```

二进制否运算可以翻转当前设置，即原设置为`0`，运算后变为`1`；原设置为`1`，运算后变为`0`。

```
flags = ~flags;
```
