## 12.運算符
> 原文: [http://exploringjs.com/impatient-js/ch_operators.html](http://exploringjs.com/impatient-js/ch_operators.html)
>
> 貢獻者:[飛龍](https://github.com/wizardforcel)
### 12.1 理解運算符
JavaScript 的運算符可能看起來很古怪。使用以下兩個規則,它們更容易理解:
* 運算符將其操作數強制轉換為適當的類型
* 大多數運算符只處理原始值
#### 12.1.1 運算符將其操作數強制轉換為適當的類型
如果運算符獲取的操作數不具有正確的類型,則很少會拋出異常。相反,它*強制轉換*(自動轉換)操作數,以便它可以使用它們。我們來看兩個例子。
首先,乘法運算符只能用于數字。因此,它在計算結果之前將字符串轉換為數字。
```js
> '7' * '3'
21
```
其次,用于訪問對象屬性的方括號運算符(`[ ]`)只能處理字符串和符號。所有其他值都強制轉換為字符串:
```js
const obj = {};
obj['true'] = 123;
// Coerce true to the string 'true'
assert.equal(obj[true], 123);
```
#### 12.1.2 大多數運算符只處理原始值
如前所述,大多數運算符僅處理原始值。如果操作數是對象,則通常將其強制轉換為原始值。例如:
```js
> [1,2,3] + [4,5,6]
'1,2,34,5,6'
```
為什么?加法運算符首先將其操作數強制轉換為原始值:
```js
> String([1,2,3])
'1,2,3'
> String([4,5,6])
'4,5,6'
```
接下來,它連接兩個字符串:
```js
> '1,2,3' + '4,5,6'
'1,2,34,5,6'
```
### 12.2 加法運算符(`+`)
加法運算符在 JavaScript 中如下工作:
* 首先,它將兩個操作數轉換為原始值。然后它切換到以下兩種模式之一:
* 字符串模式:如果兩個原始值中的一個是字符串,則它將另一個轉換為字符串,連接兩個字符串并返回結果。
* 數字模式:否則,它將兩個操作數轉換為數字,將它們相加并返回結果。
字符串模式讓我們使用`+`來組合字符串:
```js
> 'There are ' + 3 + ' items'
'There are 3 items'
```
數字模式意味著如果操作數都不是字符串(或字符串對象),那么所有內容都被強制轉換為數字:
```js
> 4 + true
5
```
`Number(true)`是`1`。
### 12.3 賦值運算符
#### 12.3.1 普通賦值運算符
普通賦值運算符用于更改存儲位置:
```js
x = value; // assign to a previously declared variable
obj.propKey = value; // assign to a property
arr[index] = value; // assign to an Array element
```
變量聲明中的初始值設定也可以視為賦值形式:
```js
const x = value;
let y = value;
```
#### 12.3.2 復合賦值運算符
給定運算符`op`,以下兩種賦值方式是等效的:
```js
myvar op= value
myvar = myvar op value
```
例如,如果`op`是`+`,那么我們得到如下工作的運算符`+=`。
```js
let str = '';
str += '<b>';
str += 'Hello!';
str += '</b>';
```
#### 12.3.3 所有復合賦值運算符的列表
* 算術運算符:
```js
+= -= *= /= %= **=
```
`+=`也適用于字符串連接
* 按位運算符:
```js
<<= >>= >>>= &= ^= |=
```
### 12.4 相等:`==`與`===`
JavaScript 有兩種相等運算符:松散相等(`==`)和嚴格相等(`===`)。建議總是使用后者。
#### 12.4.1 松散相等(`==`和`!=`)
松散相等是 JavaScript 的怪癖之一。它經常強制轉換。其中一些強制轉換是有道理的:
```js
> '123' == 123
true
> false == 0
true
```
其他不是如此:
```js
> '' == 0
true
```
當且僅當另一個操作數是原始的,對象被強制轉換為原始值:
```js
> [1, 2, 3] == '1,2,3'
true
> ['1', '2', '3'] == '1,2,3'
true
```
如果兩個操作數都是對象,則它們只有是相同的對象時才相等:
```js
> [1, 2, 3] == ['1', '2', '3']
false
> [1, 2, 3] == [1, 2, 3]
false
> const arr = [1, 2, 3];
> arr == arr
true
```
最后,`==`認為`undefined`和`null`相等:
```js
> undefined == null
true
```
 **`==`** 的其他名稱
* [*抽象相等比較*](https://tc39.github.io/ecma262/#sec-abstract-equality-comparison)是語言規范中`==`的正式名稱。
* *雙等*是它的另一個名字。
#### 12.4.2 嚴格相等(`===`和`!==`)
嚴格相等永遠不會強制轉換。僅當兩個值具有相同的類型,它們才相等。讓我們重新審視我們之前與`==`運算符的交互,看看`===`運算符的作用:
```js
> false === 0
false
> '123' === 123
false
```
當且僅當兩個值是同一個對象,則一個對象才等于另一個值:
```js
> [1, 2, 3] === '1,2,3'
false
> ['1', '2', '3'] === '1,2,3'
false
> [1, 2, 3] === ['1', '2', '3']
false
> [1, 2, 3] === [1, 2, 3]
false
> const arr = [1, 2, 3];
> arr === arr
true
```
`===`運算符不認為`undefined`和`null`相等:
```js
> undefined === null
false
```
 **`===`** 的另一個名稱
*三等*是`===`的另一個名稱。
#### 12.4.3 建議:始終使用嚴格相等
我建議總是使用`===`。它使您的代碼更容易理解,并使您不必考慮`==`的怪癖。
讓我們看看`==`的兩個用例以及我建議做的事情。
##### 12.4.3.1 `==`的用例:比較數字或字符串
`==`允許您檢查值`x`是數字還是作為字符串的數字 - 只需一次比較:
```js
if (x == 123) {
// x is either 123 or '123'
}
```
我更喜歡以下兩種選擇之一:
```js
if (x === 123 || x === '123') ···
if (Number(x) === 123) ···
```
您第一次遇到它時也可以將`x`轉換為數字。
##### 12.4.3.2 `==`的用例:與`undefined`或`null`比較
`==`的另一個用例是檢查值`x`是`undefined`還是`null`:
```js
if (x == null) {
// x is either null or undefined
}
```
這段代碼的問題在于,你無法確定是否有人打算以這種方式編寫,或者是否他們輸錯了并且意思是`=== null`。
我更喜歡以下兩種選擇之一:
```js
if (x === undefined || x === null) ···
if (x) ···
```
第二種選擇比使用`==`更加草率,但它在 JavaScript 中是一種成熟的模式(將在[布爾值](ch_booleans.html#falsiness-truthiness)的章節中詳細解釋,我們在其中看到真實性和虛假性)。
#### 12.4.4 甚至比`===`更嚴格:`Object.is()`
方法`Object.is()`比較兩個值:
```js
> Object.is(123, 123)
true
> Object.is(123, '123')
false
```
它甚至比`===`更嚴格。例如,它認為`NaN`,[數值計算](ch_numbers.html#nan)的錯誤值等于它自己:
```js
> Object.is(NaN, NaN)
true
> NaN === NaN
false
```
這偶爾會有用。例如,您可以使用它來實現 Array 方法`.indexOf()`的改進版本:
```js
const myIndexOf = (arr, elem) => {
return arr.findIndex(x => Object.is(x, elem));
};
```
`myIndexOf()`在數組中找到`NaN`,而`.indexOf()`不會:
```js
> myIndexOf([0,NaN,2], NaN)
1
> [0,NaN,2].indexOf(NaN)
-1
```
結果`-1`表示`.indexOf()`無法在 Array 中找到其參數。
### 12.5 順序運算符
表 3:JavaScript 的順序運算符
| 運算符 | 名稱 |
| --- | --- |
| `<` | 小于 |
| `<=` | 小于等于 |
| `>` | 大于 |
| `>=` | 大于等于 |
JavaScript 的順序運算符(表 3)適用于數字和字符串:
```js
> 5 >= 2
true
> 'bar' < 'foo'
true
```
`<=`和`>=`基于嚴格相等。
 **順序運算符不適合人類語言**
順序操作符不能很好地用于比較人類語言中的文本,例如,當涉及大寫或口音時。有關詳細信息,請參閱[字符串](ch_strings.html#comparing-strings)的章節。
### 12.6 各種其他運算符
* [逗號運算符](http://speakingjs.com/es5/ch09.html#comma_operator):`a, b`
* [`void`運算符](http://speakingjs.com/es5/ch09.html#void_operator):`void 0`
* 布爾運算符,字符串,數字,對象的運算符:在本書的其他地方介紹。
 **測驗**
參見[測驗應用程序](ch_quizzes-exercises.html#quizzes)。
- I.背景
- 1.關于本書(ES2019 版)
- 2.常見問題:本書
- 3. JavaScript 的歷史和演變
- 4.常見問題:JavaScript
- II.第一步
- 5.概覽
- 6.語法
- 7.在控制臺上打印信息(console.*)
- 8.斷言 API
- 9.測驗和練習入門
- III.變量和值
- 10.變量和賦值
- 11.值
- 12.運算符
- IV.原始值
- 13.非值undefined和null
- 14.布爾值
- 15.數字
- 16. Math
- 17. Unicode - 簡要介紹(高級)
- 18.字符串
- 19.使用模板字面值和標記模板
- 20.符號
- V.控制流和數據流
- 21.控制流語句
- 22.異常處理
- 23.可調用值
- VI.模塊化
- 24.模塊
- 25.單個對象
- 26.原型鏈和類
- 七.集合
- 27.同步迭代
- 28.數組(Array)
- 29.類型化數組:處理二進制數據(高級)
- 30.映射(Map)
- 31. WeakMaps(WeakMap)
- 32.集(Set)
- 33. WeakSets(WeakSet)
- 34.解構
- 35.同步生成器(高級)
- 八.異步
- 36. JavaScript 中的異步編程
- 37.異步編程的 Promise
- 38.異步函數
- IX.更多標準庫
- 39.正則表達式(RegExp)
- 40.日期(Date)
- 41.創建和解析 JSON(JSON)
- 42.其余章節在哪里?