## 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://img.kancloud.cn/7b/17/7b17729df07100ffa1c582a9b46ac13a.svg) **`==`** 的其他名称
* [*抽象相等比较*](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
```
![](https://img.kancloud.cn/7b/17/7b17729df07100ffa1c582a9b46ac13a.svg) **`===`** 的另一个名称
*三等*是`===`的另一个名称。
#### 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
```
`<=`和`>=`基于严格相等。
![](https://img.kancloud.cn/20/ae/20ae605d8efee727618ed3803e87a6e5.svg) **顺序运算符不适合人类语言**
顺序操作符不能很好地用于比较人类语言中的文本,例如,当涉及大写或口音时。有关详细信息,请参阅[字符串](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`
* 布尔运算符,字符串,数字,对象的运算符:在本书的其他地方介绍。
![](https://img.kancloud.cn/ff/a8/ffa8e16628cad59b09c786b836722faa.svg) **测验**
参见[测验应用程序](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.其余章节在哪里?