算术运算符
算术运算符使用数值(字面量或者变量)作为操作数进行运算并返回一个数值。
标准的算术运算符就是加减乘除 +
、-
、*
和 /
。
当操作数是浮点数时,这些运算符表现得跟它们在大多数编程语言中一样(特殊要注意的是,除零会产生 Infinity
)。
运算符 | 描述 | 示例 |
---|---|---|
+ | 加法 | 1 + 1 = 2 |
- | 减法 | 2 - 1 = 1 |
* | 乘法 | 3 * 3 = 9 |
/ | 除法 | 10 / 5 = 2 |
% | 求余,返回相除后余值 | 12 % 5 = 2 |
++ | 自增(更新运算符),分为前自增和后自增 | 具体参考 更新运算符 |
-- | 自减(更新运算符),分为前自减和后自减 | 具体参考 更新运算符 |
- | 一元负值符,返回操作数的负值 | -foo |
+ | 一元正值符,若操作数在操作前非数字类型,将试图将其转换成数字类型 | +foo |
** | 指数运算符,计算 base(底数) 的 exponent(指数)次方 | 2 ** 3 = 8 |
实践示例
console.log(-9 % 2);
// -1
console.log(1 + -+(+(+-+1)));
// 2
实现指数运算符
function calculateExponent(base, exponent) {
if (exponent === 1) {
return base;
} else {
return base * calculateExponent(base, exponent - 1);
}
}
赋值运算符
一个 赋值运算符(assignment operator)将它右边操作数的值赋给它左边的操作数。
下列为 ECMAScript 标准规范的 Assignment Operator:
* = /= %= += -= <<= >>= >>>= &= ^= |= **=
运算名称 | 简写的操作符 | 分解含义 | 符号 |
---|---|---|---|
赋值 | x = y | x = y | = |
加法赋值 | x += y | x = x + y | += |
减法赋值 | x -= y | x = x - y | -= |
乘法赋值 | x *= y | x = x * y | *= |
除法赋值 | x /= y | x = x / y | /= |
求余赋值 | x %= y | x = x % y | %= |
求幂赋值 | x ** y | x = x ** y | ** |
左移位赋值 | x <<= y | x = x << y | <<= |
右移位赋值 | x >>= y | x = x >> y | >>= |
无符号右移位赋值 | x >>>= y | x = x >>> y | >>>= |
按位与赋值 | x & y | x = x & y | & |
按位异赋值 | x ^= y | x = x ^ y | ^= |
按位或赋值 | x |= y | x = x | y | | y |
逗号运算符
逗号运算符 是 二元运算符,它的操作数可以是任意类型。
它首先计算左操作数,然后计算右操作数,最后返回右操作数的值,用逗号运算符可以在一条语句中执行多个运算。
(x = 0), (y = 1), (z = 2);
// 计算结果是 2,它和下面的代码基本等价
x = 0;
y = 1;
z = 2;
用法
用于声明多个变量
var a = 1,
b = 2,
c = 3;
let x, y, z;
逗号运算符最常用的场景是在 for
循环中,这个 for
循环通常具有多个循环变量:
// for 循环中的第一个逗号是 var 语句的一部分
// 第二个逗号是逗号运算符
// 它将两个表达式(i++和j--)放在一条语句中
for (var i = 0, j = 10; i < j; i++, j--) {
console.log(i + j);
}
用于赋值
逗号运算符还可以用于赋值,在用于赋值时,逗号运算符总是返回表达式中的最后一项。
var foo = (1, 2, 3, 4, 5);
// 去掉括号会报错
console.log(foo);
// 5
# 比较运算符
比较运算符比较它的操作数并返回一个基于表达式是否为 `true` 的逻辑值。
比较运算符分为**关系运算符(Relational Operators)**和**等值运算符(Equality Operators)**。
- 操作数可以是数字,字符串,逻辑,对象值。
- 字符串比较是基于标准的字典顺序,使用 Unicode 值。
- 在多数情况下,如果两个操作数不是相同的类型, JavaScript 会尝试转换它们为恰当的类型来比较。这种行为通常发生在数字作为操作数的比较。
- 类型转换的例外是使用 `===` 和 `!==` 操作符,它们会执行严格的相等和不相等比较。这些运算符不会在检查相等之前转换操作数的类型。下面的表格描述了该示例代码中的各比较运算符。
## 关系运算符
| 运算符 | 描述 | 返回 `true` 的示例 |
| ------------- | --------------------------------------------- | ------------------ |
| 大于 `>` | 左边的操作数大于右边的操作数返回 `true` | `b > a` |
| 大于等于 `>=` | 左边的操作数大于或等于右边的操作数返回 `true` | `b >= a` `a >= 1` |
| 小于 `<` | 左边的操作数小于右边的操作数返回 `true` | `a < b` `1 < 2` |
| 小于等于 `<=` | 左边的操作数小于或等于右边的操作数返回 `true` | `a <= b` `b <= 5` |
## 等值运算符
| 运算符 | 描述 | 返回 `true` 的示例 |
| ------------ | --------------------------------------- | ------------------------------ |
| 等于 `==` | 如果两边操作数相等时返回 `true` | `a == 1` `'1' == 2` `1 == '1'` |
| 不等于 `!=` | 如果两边操作数不相等时返回 `true` | `a != 2` `b != '1'` |
| 全等 `===` | 两边操作数相等且类型相同时返回 `true` | `a === 1` |
| 不全等 `!==` | 两边操作数不相等或类型不同时返回 `true` | `a !== '1'` `1 !== '1'` |
### 抽象相等比较算法
> 1. 若 Type(x) 与 Type(y) 相同,则
> 1. 若 Type(x) 为 Undefined,返回 `true`
> 2. 若 Type(x) 为 Null,返回 `true`
> 3. 若 Type(x) 为 Number,则
> 1. 若 x 为 NaN,返回 `false`
> 2. 若 y 为 NaN,返回 `false`
> 3. 若 x 与 y 为相等数值,返回 `true`
> 4. 若 x 为 +0 且 y 为 -0,返回 `true`
> 5. 若 x 为 -0 且 y 为 +0,返回 `true`
> 6. 返回 `false`
> 4. 若 Type(x) 为 String
> 1. 当 x 和 y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 `true`
> 2. 否则,返回 `false`
> 5. 若 Type(x) 为 Boolean
> 1. 当 x 和 y 为同为 `true` 或者同为 `false` 时返回 `true`
> 2. 否则,返回 `false`
> 6. 当 x 和 y 为引用用一对象时返回 `true`。否则,返回 `false`。
> 2. 若 x 为 null 且 y 为 undefined,返回 `true`
> 3. 若 x 为 undefined 且 y 为 null,返回 `true`
> 4. 若 Type(x) 为 Number 且 Type(y) 为 String,返回比较 `x == ToNumber(y)` 的结果
> 5. 若 Type(x) 为 String 且 Type(y) 为 Number,返回比较 `ToNumber(x) == y` 的结果
> 6. 若 Type(x) 为 Boolean,返回比较 `ToNumber(x) == y` 的结果
> 7. 若 Type(y) 为 Boolean,返回比较 `x == ToNumber(y)` 的结果
> 8. 若 Type(x) 为 String 或 Number,且 Type(y) 为 Object,返回比较 `x == ToPrimitive(y)` 的结果
> 9. 若 Type(x) 为 Object 且 Type(y) 为 String 或 Number,返回比较 `ToPrimitive(x) == y` 的结果
> 10. 返回 `false`
按以上相等之定义:
- 字符串比较可以按这种方式强制执行:`'' + a == '' + b`
- 数值比较可以按这种方式强制执行:`+a == +b`
- 布尔值比较可以按这种方式强制执行:`!a == !b`
等值比较操作保证以下不变:
- `A !== B` 等价于 `!(A == B)`
- `A == B` 等价于 `B == A`,除了 A 与 B 的执行顺序。
相等运算符不总是传递的。例如,两个不同的 String 对象,都表示相同的字符串值;`==` 运算符认为每个 `String` 对象都与字符串值相等,但是两个字符串对象互不相等。
- `new String('a') == 'a'` 和 `'a' == new String('a')` 皆为 `true`
- `new String('a') == new String('a')` 为 `false`
字符串比较使用的方式是简单地检测字符编码单元序列是否相同。不会做更复杂的、基于语义的字符或者字符串相等的定义以及 Unicode 规范中定义的 Collating Order。所以 Unicode 标准中认为相等的 String 值可能被检测为不等。实际上这一算法认为两个字符串已经是经过规范化的形式。
### 引用数据类型间比较
```js
const a = function() {};
const b = function() {};
console.log(a === b);
// false
console.log([] === []);
// false
console.log({} === {});
// false
当我们访问引用数据类型(对象、数组、函数等等)的值时,首先从栈中获得该对象的 地址指针,然后再从 堆内存 中取得所需的数据。
变量 a
实际保存的是指向堆内存中对象的一个指针,而变量 b
保存的是指向堆内存中的另一个对象的指针,虽然这两个对象的值时一样的,但它们是独立的两个对象,占了两份内存空间,所以它们互不相等。
而当将一个为引用数据类型的值的变量赋值给另一个变量时,即拷贝了前者的内存空间的地址指针,因此它们都指向堆内存中同一个对象。
let x = {}
let y = x
console.log(x === y
// true