Skip to content

运算符全谱

基于现代 JavaScript(ES2025 · 核于 2026-06)

速查

  • 算术:+ - * / %,外加指数 **(ES2016,右结合,2 ** 3 ** 2 === 512
  • 自增自减:++x 先变后取、x++ 先取后变;-x 取负、+x 转数字
  • 赋值:= += -= *= /= %= **=,位运算赋值,逻辑赋值 &&= ||= ??=(ES2021)
  • 比较:=== !==(不转换,优先)、== !=(转换,避免)、< > <= >=
  • 逻辑 && || ! 返回操作数本身而非布尔,且短路求值a && ba || b
  • ?? 空值合并(ES2020):仅 null / undefined 时取右值,区别于 ||(对一切 falsy 兜底)
  • ?. 可选链(ES2020):a?.ba?.[k]a?.(),中途为空则短路返回 undefined
  • 位运算 & | ^ ~ << >> >>>:先转 32 位整数;~x === -x - 1>>> 对 BigInt 无定义
  • 三元 cond ? a : b;逗号 a, b 取最后一个值
  • 优先级:** > * / % > + - > 比较 > && > || / ?? > ? : > =?? 不能与 ||/&& 不加括号混用

算术运算符

js
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 次方,右结合

js
2 ** 10; // 1024
2 ** -1; // 0.5
2 ** 3 ** 2; // 512(右结合:先 3**2=9,再 2**9)
// 负数底数需加括号:(-2) ** 2 === 4;-2 ** 2 是语法错误

自增 / 自减与一元正负

前缀「先改后取值」,后缀「先取值后改」:

js
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))

赋值运算符

js
let x = 10;
x += 3; // x = x + 3 → 13
x -= 1; // 12
x *= 2; // 24
x /= 4; // 6
x %= 4; // 2
x **= 3; // 8

字符串也能 += 拼接:

js
let s = "alpha";
s += "bet"; // "alphabet"

逻辑赋值 &&= / ||= / ??=(ES2021)

把逻辑运算与赋值合并,且带短路——仅在需要时才赋值:

js
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 仍是 0

x ??= y 等价 x ?? (x = y)——x 已有非空值就不触碰,常用于「只在没设置过时给默认」。

比较运算符

=== / !== 不做类型转换(首选),== / != 会转换(上一页已详述,尽量避免):

js
1 === 1; // true
1 === "1"; // false
1 !== "1"; // true

关系比较 < > <= >= 对字符串按 Unicode 码位逐字符比较,对数字按大小;混合类型会转数字:

js
2 < 12; // true
"2" < "12"; // false ← 字符串按字典序:"2" > "1"
"apple" < "banana"; // true(逐字符比较)
"12" > 9; // true("12" 转数字 12)

null / undefined 的关系比较陷阱

关系运算符与 ==null 的处理不一致,导致诡异结果:

js
null == 0; // false(== 不把 null 转 0)
null >= 0; // true(>= 把 null 转成数字 0)
null > 0; // false
null === 0; // false

null >= 0 为真但 null == 0 为假——因为关系运算符走数字转换(null → 0),而 ==null 有特殊规则。结论同前:少用这些隐式比较。

逻辑运算符:返回值 + 短路

&&||! 的关键认知:它们返回操作数本身,不一定是布尔值,并且短路求值

&& 逻辑与

a && ba 为 falsy 则返回 a(短路,不算 b);否则返回 b

js
"Cat" && "Dog"; // "Dog"(前者真 → 返回后者)
0 && "Dog"; // 0(前者假 → 短路返回 0,"Dog" 不执行)
null && fn(); // null(fn 不会被调用——短路防止副作用)

|| 逻辑或

a || ba 为 truthy 则返回 a(短路);否则返回 b。常用于设默认值(但注意 falsy 陷阱):

js
"Cat" || "Dog"; // "Cat"(前者真 → 短路返回)
"" || "默认名"; // "默认名"(前者假 → 返回后者)
0 || 100; // 100 ← 但若 0 是合法值,这就错了!该用 ??

! 逻辑非

js
!true; // false
!""; // true(空串 falsy)
!!"abc"; // true(双非 = 转布尔)

?? 空值合并(ES2020)

a ?? b仅当 anullundefined 时返回 b,否则返回 a。这是它与 || 的本质区别——|| 会对一切 falsy(含 0 / "" / false)兜底,?? 只认「空」:

js
null ?? "默认"; // "默认"
undefined ?? "默认"; // "默认"
0 ?? "默认"; // 0 ←(|| 会得到 "默认",?? 保留 0)
"" ?? "默认"; // ""(保留空串)
false ?? "默认"; // false(保留 false)

0 / "" / false 是合法值时,?? 不用 || 设默认:

js
const port = config.port ?? 3000; // port 为 0 也保留(0 是合法端口语义时)
const name = input || "匿名"; // 输入空串想兜底 → 这里用 || 才对

?? 不能与 || / && 直接混用

为避免歧义,??||&& 不加括号写在一起是语法错误

js
a || b ?? c; // ❌ SyntaxError
(a || b) ?? c; // ✅ 必须显式加括号

?. 可选链(ES2020)

a?.b:若 anull / undefined短路返回 undefined,不再继续取值、也不抛 TypeError。三种形态:

js
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)再兜底:

js
const city = user?.address?.city ?? "未知";

位运算符

操作数先转成 32 位有符号整数,逐位运算:

js
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 无固定位宽)。

三元与逗号运算符

js
// 三元:唯一接受三个操作数的运算符
const status = age >= 18 ? "成年" : "未成年";

// 逗号:从左到右求值,返回最后一个,主要用于 for 多变量
for (let i = 0, j = 9; i < j; i++, j--) {
  /* ... */
}
const last = (1, 2, 3); // 3

类型 / 成员相关运算符

js
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
相等== != === !==
逻辑与&&
逻辑或 / 空合并|| ??(二者不可不加括号混用)
三元? :
赋值= += ... &&= ||= ??=(右结合)
js
1 + 2 * 3; // 7(* 优先)
(1 + 2) * 3; // 9(括号改变优先级)
!true === false; // true(! 先于 ===:!true=false,false===false)

不确定时加括号——可读性永远比省字符重要。

小结

逻辑运算符返回操作数本身且短路,???. 让默认值与防御取值变得简洁安全,** 是右结合的指数,位运算先转 32 位整数。记住 ?? 不能与 || / && 裸混、优先级拿不准就加括号。掌握运算符后,下一页把它们组织进 控制流与循环