运算符全谱
基于现代 JavaScript(ES2025 · 核于 2026-06)
速查
- 算术:
+ - * / %,外加指数**(ES2016,右结合,2 ** 3 ** 2 === 512) - 自增自减:
++x先变后取、x++先取后变;-x取负、+x转数字 - 赋值:
= += -= *= /= %= **=,位运算赋值,逻辑赋值&&= ||= ??=(ES2021) - 比较:
=== !==(不转换,优先)、== !=(转换,避免)、< > <= >= - 逻辑
&& || !返回操作数本身而非布尔,且短路求值:a && b、a || b ??空值合并(ES2020):仅null/undefined时取右值,区别于||(对一切 falsy 兜底)?.可选链(ES2020):a?.b、a?.[k]、a?.(),中途为空则短路返回undefined- 位运算
& | ^ ~ << >> >>>:先转 32 位整数;~x === -x - 1;>>>对 BigInt 无定义 - 三元
cond ? a : b;逗号a, b取最后一个值 - 优先级:
**>* / %>+ -> 比较 >&&>||/??>? :>=;??不能与||/&&不加括号混用
算术运算符
7 + 3; // 10
7 - 3; // 4
7 * 3; // 21
7 / 3; // 2.333...(始终浮点除法,无整除)
7 % 3; // 1(余数,符号随被除数:-7 % 3 === -1)
1 / 0; // Infinity(除零不报错)指数运算符 **(ES2016)
a ** b 即 a 的 b 次方,右结合:
2 ** 10; // 1024
2 ** -1; // 0.5
2 ** 3 ** 2; // 512(右结合:先 3**2=9,再 2**9)
// 负数底数需加括号:(-2) ** 2 === 4;-2 ** 2 是语法错误自增 / 自减与一元正负
前缀「先改后取值」,后缀「先取值后改」:
let x = 5;
console.log(x++); // 5(先返回旧值,x 才变 6)
console.log(x); // 6
console.log(++x); // 7(先变 7,再返回)
const n = "8";
console.log(-n); // -8(一元负,先转数字)
console.log(+n); // 8(一元正 = Number(n))赋值运算符
let x = 10;
x += 3; // x = x + 3 → 13
x -= 1; // 12
x *= 2; // 24
x /= 4; // 6
x %= 4; // 2
x **= 3; // 8字符串也能 += 拼接:
let s = "alpha";
s += "bet"; // "alphabet"逻辑赋值 &&= / ||= / ??=(ES2021)
把逻辑运算与赋值合并,且带短路——仅在需要时才赋值:
let a = 0;
a ||= 5; // a 为 falsy(0) → 赋值,a = 5
a &&= 9; // a 为 truthy(5) → 赋值,a = 9
let b = null;
b ??= "默认"; // b 为 nullish → 赋值,b = "默认"
let c = 0;
c ??= 10; // c 是 0(非 nullish)→ 不赋值,c 仍是 0x ??= y 等价 x ?? (x = y)——x 已有非空值就不触碰,常用于「只在没设置过时给默认」。
比较运算符
=== / !== 不做类型转换(首选),== / != 会转换(上一页已详述,尽量避免):
1 === 1; // true
1 === "1"; // false
1 !== "1"; // true关系比较 < > <= >= 对字符串按 Unicode 码位逐字符比较,对数字按大小;混合类型会转数字:
2 < 12; // true
"2" < "12"; // false ← 字符串按字典序:"2" > "1"
"apple" < "banana"; // true(逐字符比较)
"12" > 9; // true("12" 转数字 12)null / undefined 的关系比较陷阱
关系运算符与 == 对 null 的处理不一致,导致诡异结果:
null == 0; // false(== 不把 null 转 0)
null >= 0; // true(>= 把 null 转成数字 0)
null > 0; // false
null === 0; // falsenull >= 0 为真但 null == 0 为假——因为关系运算符走数字转换(null → 0),而 == 对 null 有特殊规则。结论同前:少用这些隐式比较。
逻辑运算符:返回值 + 短路
&&、||、! 的关键认知:它们返回操作数本身,不一定是布尔值,并且短路求值。
&& 逻辑与
a && b:a 为 falsy 则返回 a(短路,不算 b);否则返回 b:
"Cat" && "Dog"; // "Dog"(前者真 → 返回后者)
0 && "Dog"; // 0(前者假 → 短路返回 0,"Dog" 不执行)
null && fn(); // null(fn 不会被调用——短路防止副作用)|| 逻辑或
a || b:a 为 truthy 则返回 a(短路);否则返回 b。常用于设默认值(但注意 falsy 陷阱):
"Cat" || "Dog"; // "Cat"(前者真 → 短路返回)
"" || "默认名"; // "默认名"(前者假 → 返回后者)
0 || 100; // 100 ← 但若 0 是合法值,这就错了!该用 ??! 逻辑非
!true; // false
!""; // true(空串 falsy)
!!"abc"; // true(双非 = 转布尔)?? 空值合并(ES2020)
a ?? b:仅当 a 是 null 或 undefined 时返回 b,否则返回 a。这是它与 || 的本质区别——|| 会对一切 falsy(含 0 / "" / false)兜底,?? 只认「空」:
null ?? "默认"; // "默认"
undefined ?? "默认"; // "默认"
0 ?? "默认"; // 0 ←(|| 会得到 "默认",?? 保留 0)
"" ?? "默认"; // ""(保留空串)
false ?? "默认"; // false(保留 false)当 0 / "" / false 是合法值时,用 ?? 不用 || 设默认:
const port = config.port ?? 3000; // port 为 0 也保留(0 是合法端口语义时)
const name = input || "匿名"; // 输入空串想兜底 → 这里用 || 才对?? 不能与 || / && 直接混用
为避免歧义,?? 与 || 或 && 不加括号写在一起是语法错误:
a || b ?? c; // ❌ SyntaxError
(a || b) ?? c; // ✅ 必须显式加括号?. 可选链(ES2020)
a?.b:若 a 是 null / undefined 则短路返回 undefined,不再继续取值、也不抛 TypeError。三种形态:
const user = { profile: { name: "Ada" } };
user?.profile?.name; // "Ada"
user?.address?.city; // undefined(address 不存在,短路,不报错)
user?.["profile"]?.["name"]; // "Ada"(方括号形态)
user.save?.(); // undefined(save 不存在则不调用,方法形态)
// 没有可选链时,要写一长串防御:
user && user.address && user.address.city; // 旧写法可选链常配空值合并
a?.b ?? 默认值 是高频组合:先安全取值,取不到(undefined)再兜底:
const city = user?.address?.city ?? "未知";位运算符
操作数先转成 32 位有符号整数,逐位运算:
15 & 9; // 9 (1111 & 1001 = 1001)按位与
15 | 9; // 15 (1111 | 1001 = 1111)按位或
15 ^ 9; // 6 (1111 ^ 1001 = 0110)按位异或
~15; // -16 按位非,~x === -x - 1
9 << 2; // 36 左移(×2^2)
9 >> 2; // 2 有符号右移(保留符号位)
-9 >> 2; // -3 符号位填充
19 >>> 2; // 4 无符号右移(高位补 0)要点:~x 等于 -x - 1(故 ~~x 常被当作「向零取整」的 hack,但 Math.trunc 更清晰);>>>(无符号右移)对 BigInt 无定义(BigInt 无固定位宽)。
三元与逗号运算符
// 三元:唯一接受三个操作数的运算符
const status = age >= 18 ? "成年" : "未成年";
// 逗号:从左到右求值,返回最后一个,主要用于 for 多变量
for (let i = 0, j = 9; i < j; i++, j--) {
/* ... */
}
const last = (1, 2, 3); // 3类型 / 成员相关运算符
typeof 42; // "number"(见原始类型页)
[] instanceof Array; // true(原型链上是否有该构造函数)
"name" in { name: 1 }; // true(属性是否存在,含继承)
0 in [9, 8]; // true(数组索引 0 存在)
delete obj.prop; // 删除属性,返回 true / false运算符优先级(常用部分)
从高到低,常见的几档(完整表见 MDN):
| 档位 | 运算符 |
|---|---|
| 最高 | () 分组、?. 成员访问 |
| 一元 | ! ~ +(一元)-(一元)typeof ++ -- |
| 指数 | **(右结合) |
| 乘除 | * / % |
| 加减 | + - |
| 移位 | << >> >>> |
| 关系 | < > <= >= instanceof in |
| 相等 | == != === !== |
| 逻辑与 | && |
| 逻辑或 / 空合并 | || ??(二者不可不加括号混用) |
| 三元 | ? : |
| 赋值 | = += ... &&= ||= ??=(右结合) |
1 + 2 * 3; // 7(* 优先)
(1 + 2) * 3; // 9(括号改变优先级)
!true === false; // true(! 先于 ===:!true=false,false===false)不确定时加括号——可读性永远比省字符重要。
小结
逻辑运算符返回操作数本身且短路,?? 与 ?. 让默认值与防御取值变得简洁安全,** 是右结合的指数,位运算先转 32 位整数。记住 ?? 不能与 || / && 裸混、优先级拿不准就加括号。掌握运算符后,下一页把它们组织进 控制流与循环。