Web/04-JavaScript基础/10-运算符.md
2021-12-07 17:08:46 +08:00

626 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 10-运算符
publish: true
---
<ArticleTopAd></ArticleTopAd>
我们在前面讲过变量,本文讲一下**运算符**和表达式。
## 运算符的定义和分类
### 运算符的定义
**运算符**:也叫操作符,是一种符号。通过运算符可以对一个或多个值进行运算,并获取运算结果。
**表达式**:数字、运算符、变量的组合(组成的式子)。
表达式最终都会有一个运算结果,我们将这个结果称为表达式的**返回值**。
比如:`+`、`*`、`/`、`(` 都是**运算符**,而`3+5/2`则是**表达式**。
比如typeof 就是运算符,可以来获得一个值的类型。它会将该值的类型以**字符串**的形式返回,返回值可以是 number、string、boolean、undefined、object。
### 运算符的分类
JS 中的运算符,分类如下:
- 算数运算符
- 自增/自减运算符
- 一元运算符
- 逻辑运算符
- 赋值运算符
- 比较运算符
- 三元运算符(条件运算符)
下面来逐一讲解。
## 算数运算符
**算术运算符**:用于执行两个变量或值的算术运算。
常见的算数运算符有以下几种:
| 运算符 | 描述 |
| :----- | :--------------------: |
| + | 加、字符串连接 |
| - | 减 |
| \* | 乘 |
| / | 除 |
| % | 获取余数(取余、取模) |
**求余的举例**
假设用户输入 345怎么分别得到 3、4、5 这三个数呢?
**答案**
```
得到3的方法345 除以100得到3.45然后取整得到3。即parseInt(345/100)
得到4的方法345 除以100余数是45除以10得到4.5取整。即parseInt(345 % 100 / 10)
得到5的方法345 除以10余数就是5。即345 % 10
```
### 算数运算符的运算规则
1先算乘除、后算加减。
2小括号`( )`:能够影响计算顺序,且可以嵌套。没有中括号、没有大括号,只有小括号。
3百分号取余。只关心余数。
举例 1取余
```javascript
console.log(3 % 5);
```
输出结果为 3。
举例 2注意运算符的优先级
```javascript
var a = 1 + ((2 * 3) % 4) / 3;
```
结果分析:
原式 = 1 + 6 % 4 / 3 = 1 + 2 / 3 = 1.66666666666666
**补充**关于算数运算符的注意事项详见前面的文章《JavaScript 基础/typeof 和数据类型转换》里的“**隐式类型转换**”。
### 取模(取余)运算
格式:
```js
余数 = m % n;
```
计算结果:
- 如果 n < 0那就先把 n 取绝对值后再计算等价于 m % (-n)。
- 如果 n 0那么结果是 NaN
- n > 0 的情况下:
- 如果 m>=n那就正常取余。
- 如果 m<n那结果就是 m
- 取余运算结果的正负性取决于 m而不是 n比如`10 % -3`的运算结果是 1。`-10 % 3`的运算结果是-1
### 浮点数运算的精度问题
浮点数值的最高精度是 17 位小数但在进行算术计算时会丢失精度导致计算不够准确比如
```javascript
console.log(0.1 + 0.2); // 运算结果不是 0.3,而是 0.30000000000000004
console.log(0.07 * 100); // 运算结果不是 7而是 7.000000000000001
```
因此**不要直接判断两个浮点数是否相等**。前面的文章JavaScript 基础基本数据类型Number有详细介绍
## 自增和自减
### 自增 `++`
自增分成两种`a++``++a`。
1一个变量自增以后原变量的值会**立即**自增 1也就是说无论是 `a++` 还是`++a`都会立即使原变量的值自增 1
2**我们要注意的是**`a`是变量`a++``++a`**表达式**。
那这两种自增有啥区别呢区别是`a++` `++a`的值不同也就是说表达式的值不同
- `a++`这个表达式的值等于原变量的值a 自增前的值)。你可以这样理解先把 a 的值赋值给表达式然后 a 再自增
- `++a`这个表达式的值等于新值 a 自增后的值)。 你可以这样理解a 先自增然后再把自增后的值赋值给表达式
### 自减 `-- `
原理同上
开发时大多使用后置的自增/自减并且代码独占一行例如`num++`或者 `num--`
### 代码举例
```javascript
var n1 = 10;
var n2 = 20;
var result1 = n1++;
console.log(n1); // 11
console.log(result1); // 10
result = ++n1;
console.log(n1); //12
console.log(result); //12
var result2 = n2--;
console.log(n2); // 19
console.log(result2); // 20
result2 = --n2;
console.log(n2); // 18
console.log(result2); // 18
```
## 一元运算符
一元运算符只需要一个操作数
常见的一元运算符如下
### typeof
typeof 就是典型的一元运算符因为后面只跟一个操作数
举例如下
```javascript
var a = '123';
console.log(typeof a); // 打印结果string
```
### 正号 `+`
1正号不会对数字产生任何影响比如说`2``+2`是一样的
2我们可以对一个其他的数据类型使用`+`来将其转换为 number重要的小技巧】。比如
```javascript
var a = true;
a = +a; // 注意这行代码的一元运算符操作
console.log('a' + a);
console.log(typeof a);
console.log('-----------------');
var b = '18';
b = +b; // 注意这行代码的一元运算符操作
console.log('b' + b);
console.log(typeof b);
```
打印结果
```
a1
number
-----------------
b18
number
```
### 负号 `-`
负号可以对数字进行取反
## 逻辑运算符
逻辑运算符有三个
- `&&` 两个都为真结果才为真
- `||` 只要有一个是真结果就是真
- `!` 对一个布尔值进行取反
注意能参与逻辑运算的都是布尔值
**连比的写法:**
来看看逻辑运算符连比的写法
举例 1
```javascript
console.log(3 < 2 && 2 < 4);
```
输出结果为 false
举例 2判断一个人的年龄是否在 18~65 岁之间
```javascript
const a = prompt('请输入您的年龄');
if (a >= 18 && a < 65) {
alert('可以上班');
} else {
alert('准备退休');
}
```
PS上面的`a>=18 && a<= 65`千万别想当然地写成` 18<= a <= 65`,没有这种语法。
### 非布尔值的与或运算【重要】
> 之所以重要,是因为在实际开发中,我们经常用这种代码做容错处理或者兜底处理。
非布尔值进行**与或运算**时,会先将其转换为布尔值,然后再运算,但返回结果是**原值**。比如说:
```javascript
var result = 5 && 6; // 运算过程true && true;
console.log('result' + result); // 打印结果6也就是说最后面的那个值。
```
上方代码可以看到,虽然运算过程为布尔值的运算,但返回结果是原值。
那么,返回结果是哪个原值呢?我们来看一下。
**与运算**的返回结果:(以多个非布尔值的运算为例)
- 如果第一个值为 false则执行第一条语句并直接返回第一个值不会再往后执行。
- 如果第一个值为 true则继续执行第二条语句并返回第二个值如果所有的值都为 true则返回的是最后一个值
**或运算**的返回结果:(以多个非布尔值的运算为例)
- 如果第一个值为 true则执行第一条语句并直接返回第一个值不会再往后执行。
- 如果第一个值为 false则继续执行第二条语句并返回第二个值如果所有的值都为 false则返回的是最后一个值
实际开发中,我们经常是这样来做「容错处理」的:
当前端成功调用一个接口后,返回的数据为 result 对象。这个时候,我们用变量 a 来接收 result 里的图片资源。通常的写法是这样的:
```javascript
if (result.resultCode == 0) {
var a = result && result.data && result.data.imgUrl || 'http://img.smyhvae.com/20160401_01.jpg';
}
```
上方代码的意思是,获取返回结果中的`result.data.imgUrl`这个图片资源;如果返回结果中没有 `result.data.imgUrl` 这个字段,就用 `http://img.smyhvae.com/20160401_01.jpg` 作为**兜底**图片。这种写法,在实际开发中经常用到。
### 非布尔值的 `!` 运算
非布尔值进行**非运算**时,会先将其转换为布尔值,然后再运算,返回结果是**布尔值**。
举例:
```javascript
let a = 10;
a = !a;
console.log(a); // false
console.log(typeof a); // boolean
```
### 短路运算的妙用【重要】
> 下方举例中的写法技巧,在实际开发中,经常用到。这种写法,是一种很好的「容错、容灾、降级」方案,需要多看几遍。
1、JS 中的`&&`属于**短路**的与:
- 如果第一个值为 false则不会执行后面的内容。
- 如果第一个值为 true则继续执行第二条语句并返回第二个值。
举例:
```javascript
const a1 = 'qianguyihao';
// 第一个值为true会继续执行后面的内容
a1 && alert('看 a1 出不出来'); // 可以弹出 alert 框
const a2 = undefined;
// 第一个值为false不会继续执行后面的内容
a2 && alert('看 a2 出不出来'); // 不会弹出 alert 框
```
2、JS 中的`||`属于**短路**的或:
- 如果第一个值为 true则不会执行后面的内容。
- 如果第一个值为 false则继续执行第二条语句并返回第二个值。
举例:
```js
const result; // 请求接口时,后台返回的内容
let errorMsg = ''; // 前端的文案提示
if (result && result.retCode != 0) {
// 接口返回异常码时
errorMsg = result.msg || '活动太火爆,请稍后再试'; // 文案提示信息,优先用 接口返回的msg字段其次用 '活动太火爆,请稍后再试' 这个文案兜底。
}
if (!result) {
// 接口挂掉时
errorMsg = '网络异常,请稍后再试';
}
```
## 赋值运算符
赋值:将符号右侧的值赋给符号左侧的变量。
### 赋值运算符包括哪些
- `=` 直接赋值。比如 `var a = 5`。意思是,把 5 这个值,往 a 里面存一份。简称:把 5 赋值给 a。
- `+=`。a += 5 等价于 a = a + 5
- `-=`。a -= 5 等价于 a = a - 5
- `*=`。a _ = 5 等价于 a = a _ 5
- `/=`。a /= 5 等价于 a = a / 5
- `%=`。a %= 5 等价于 a = a % 5
### 赋值运算符的注意事项
todo
## 比较运算符
比较运算符可以比较两个值之间的大小关系,如果关系成立它会返回 true如果关系不成立则返回 false。
比较运算符有很多种,比如:
```
> 大于号
< 小于号
>= 大于或等于
<= 小于或等于
== 等于
=== 全等于
!= 不等于
!== 不全等于
```
**比较运算符,得到的结果都是布尔值:要么是 true要么是 false**
举例如下:
```javascript
var result = 5 > 10; // false
```
### 非数值的比较
1对于非数值进行比较时会将其转换为数字然后再比较。
举例如下:
```javascript
console.log(1 > true); //false
console.log(1 >= true); //true
console.log(1 > '0'); //true
//console.log(10 > null); //true
//任何值和NaN做任何比较都是false
console.log(10 <= 'hello'); //false
console.log(true > false); //true
```
2特殊情况如果符号两侧的值都是字符串时**不会**将其转换为数字进行比较。比较两个字符串时,比较的是字符串的**Unicode 编码**。【非常重要,这里是个大坑,很容易踩到】
比较字符编码时,是一位一位进行比较。如果两位一样,则比较下一位。
比如说,当你尝试去比较`"123"`和`"56"`这两个字符串时,你会发现,字符串"56"竟然比字符串"123"要大(因为 5 比 1 大)。也就是说,下面这样代码的打印结果,其实是 true:(这个我们一定要注意,在日常开发中,很容易忽视)
```javascript
// 比较两个字符串时,比较的是字符串的字符编码,所以可能会得到不可预期的结果
console.log('56' > '123'); // true
```
**因此**:当我们在比较两个字符串型的数字时,**一定一定要先转型**再比较大小,比如 `parseInt()`
3任何值和 NaN 做任何比较都是 false。
### `==`符号的强调
注意`==`这个符号,它是**判断是否等于**,而不是赋值。
1`== `这个符号,还可以验证字符串是否相同。例如:
```javascript
console.log('我爱你中国' == '我爱你中国'); // 输出结果为true
```
2`== `这个符号并不严谨,会做隐式转换,将不同的数据类型,**转为相同类型**进行比较(大部分情况下,都是转换为数字)。例如:
```javascript
console.log('6' == 6); // 打印结果true。这里的字符串"6"会先转换为数字6然后再进行比较
console.log(true == '1'); // 打印结果true
console.log(0 == -0); // 打印结果true
console.log(null == 0); // 打印结果false
```
3undefined 衍生自 null所以这两个值做相等判断时会返回 true。
```javascript
console.log(undefined == null); //打印结果true。
```
4NaN 不和任何值相等,包括他本身。
```javascript
console.log(NaN == NaN); //false
console.log(NaN === NaN); //false
```
问题:那如果我想判断 b 的值是否为 NaN该怎么办呢
答案:可以通过 isNaN()函数来判断一个值是否是 NaN。举例
```javascript
console.log(isNaN(b));
```
如上方代码所示,如果 b 为 NaN则返回 true否则返回 false。
### `===`全等符号的强调
**全等在比较时,不会做类型转换**。如果要保证**绝对等于(完全等于)**,我们就要用三个等号`===`。例如:
```javascript
console.log('6' === 6); //false
console.log(6 === 6); //true
```
上述内容分析出:
- `==`两个等号,不严谨,"6"和 6 是 true。
- `===`三个等号,严谨,"6"和 6 是 false。
另外还有:**`==`的反面是`!=``===`的反面是`!==`**。例如:
```javascript
console.log(3 != 8); // true
console.log(3 != '3'); // false因为3=="3"是true所以反过来就是false。
console.log(3 !== '3'); // true应为3==="3"是false所以反过来是true。
```
## 三元运算符
三元运算符也叫条件运算符。
语法:
```
条件表达式 ? 语句1 : 语句2;
```
**执行的流程**
条件运算符在执行时,首先对条件表达式进行求值:
- 如果该值为 true则执行语句 1并返回执行结果
- 如果该值为 false则执行语句 2并返回执行结果
如果条件的表达式的求值结果是一个非布尔值,会将其转换为布尔值然后再运算。
## 运算符的优先级
运算符的优先级如下:(优先级从高到低)
- `.`、`[]`、`new`
- `()`
- `++`、`--`
- `!`、`~`、`+`(单目)、`-`(单目)、`typeof`、`void`、`delete`
- `*`、`/`、`%`
- `+`(双目)、`-`(双目)
- `<<`、`>>`、`>>>`
- 关系运算符:`<`、`<=`、`>`、`>=`
- `==`、`!==`、`===`、`!==`
- `&`
- `^`
- `|`
- `&&`
- `||`
- `?:`
- `=`、`+=`、`-=`、`*=`、`/=`、`%=`、`<<=`、`>>=`、`>>>=`、`&=`、`^=`、`|=`
- `,`
注意:逻辑与 `&&` 比逻辑或 `||` 的优先级更高。
备注:你在实际写代码的时候,如果不清楚哪个优先级更高,可以把括号用上。
## Unicode 编码
> 这一段中我们来讲引申的内容Unicode 编码的使用。
各位同学可以先在网上查一下“Unicode 编码表”。
1、在字符串中可以使用转义字符输入 Unicode 编码。格式如下:
```
\u四位编码
```
举例如下:
```javascript
console.log('\u2600'); // 这里的 2600 采用的是16进制
console.log('\u2602'); // 这里的 2602 采用的是16进制。
```
打印结果:
![](http://img.smyhvae.com/20181222_1218.png)
2、我们还可以在 HTML 网页中使用 Unicode 编码。格式如下:
```
&#四位编码;
```
PS我们知道Unicode 编码采用的是 16 进制,但是,这里的编码需要使用 10 进制。
举例如下:
```html
<h1 style="font-size: 100px;">&#9860;</h1>
```
打印结果:
![](http://img.smyhvae.com/20181222_1226.png)
## 我的公众号
想学习**更多技能**?不妨关注我的微信公众号:**千古壹号**。
扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:
![](https://img.smyhvae.com/20200102.png)