09-运算符大全
运算符大全:算术、比较、逻辑与位运算
运算符是 JavaScript 的"动词",它们让数据动起来。掌握各种运算符的用法和优先级,能让你写出更简洁、更高效的代码。
学习目标
读完本文,你将学会:
- 算术运算符、赋值运算符、比较运算符的使用
- 逻辑运算符(&&、||、!)的短路求值
- 三元运算符、空值合并运算符和可选链运算符
- 运算符优先级和结合性
一、算术运算符
1.1 基本算术运算
| 运算符 | 名称 | 示例 | 结果 |
|---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 5 - 3 | 2 |
* | 乘法 | 5 * 3 | 15 |
/ | 除法 | 5 / 2 | 2.5 |
% | 取余 | 5 % 2 | 1 |
** | 幂运算 | 2 ** 3 | 8 |
// 注意:JavaScript 的除法总是返回浮点数5/2;// 2.5(不是 2)// 取余运算7%3;// 1(7 除以 3 余 1)10%2;// 0(10 能被 2 整除)// 幂运算(ES2016)2**3;// 8(2 的 3 次方)Math.pow(2,3);// 8(等效写法)1.2 自增和自减
| 运算符 | 名称 | 示例 | 说明 |
|---|---|---|---|
++ | 自增 | i++或++i | 值加 1 |
-- | 自减 | i--或--i | 值减 1 |
leta=5;// 后置自增:先使用值,再自增letb=a++;// b = 5, 然后 a 变成 6console.log(b);// 5console.log(a);// 6letc=5;// 前置自增:先自增,再使用值letd=++c;// c 先变成 6,然后 d = 6console.log(d);// 6console.log(c);// 6建议:在独立语句中使用自增/自减,不要嵌在复杂表达式中,容易出错。
// ✅ 好的写法letcount=0;count++;// ❌ 容易混淆的写法letresult=array[i++]+array[++i]*array[i++];// 不要这样写!1.3 一元运算符
| 运算符 | 名称 | 示例 | 结果 |
|---|---|---|---|
+ | 正号 | +"5" | 5(转数字) |
- | 负号 | -5 | -5 |
+ | 数值转换 | +true | 1 |
// 正号可以快速转数字conststr="42";+str;// 42// 负号constnum=10;-num;// -10二、赋值运算符
2.1 基本赋值
letx=10;// 把 10 赋值给 x2.2 复合赋值运算符
| 运算符 | 示例 | 等效写法 |
|---|---|---|
+= | x += 5 | x = x + 5 |
-= | x -= 5 | x = x - 5 |
*= | x *= 5 | x = x * 5 |
/= | x /= 5 | x = x / 5 |
%= | x %= 5 | x = x % 5 |
**= | x **= 2 | x = x ** 2 |
letscore=100;score+=10;// score = 110score-=5;// score = 105score*=2;// score = 210score/=3;// score = 702.3 解构赋值(ES6)
// 数组解构const[a,b]=[1,2];console.log(a);// 1console.log(b);// 2// 对象解构const{name,age}={name:"小明",age:18};console.log(name);// "小明"console.log(age);// 18// 带默认值const[x,y=10]=[5];console.log(y);// 10(默认值)// 交换变量letm=1,n=2;[m,n]=[n,m];// m = 2, n = 1三、比较运算符
3.1 基本比较
| 运算符 | 名称 | 示例 | 结果 |
|---|---|---|---|
=== | 严格相等 | 5 === 5 | true |
!== | 严格不等 | 5 !== "5" | true |
== | 相等(不推荐) | 5 == "5" | true |
!= | 不等(不推荐) | 5 != "5" | false |
> | 大于 | 5 > 3 | true |
< | 小于 | 5 < 3 | false |
>= | 大于等于 | 5 >= 5 | true |
<= | 小于等于 | 5 <= 3 | false |
再次强调:始终使用===和!==,不要用==和!=。
3.2 字符串比较
字符串按字典顺序(Unicode 编码)比较:
"apple"<"banana";// true(a 在 b 前面)"10"<"2";// true("1" 的 Unicode 码小于 "2")"Z">"A";// true3.3 特殊值的比较
NaN===NaN;// falsenull===undefined;// false+0===-0;// true四、逻辑运算符
4.1 基本逻辑运算
| 运算符 | 名称 | 规则 |
|---|---|---|
&& | 逻辑与 | 两边都为真,结果才为真 |
| ` | ` | |
! | 逻辑非 | 取反 |
// &&(与)true&&true;// truetrue&&false;// falsefalse&&true;// false(左边为 false,右边不会执行)// ||(或)true||false;// truetrue||true;// truefalse||true;// truefalse||false;// false// !(非)!true;// false!false;// true!!true;// true(双非转布尔值)4.2 短路求值(重要!)
逻辑运算符不会总是计算两边,而是"短路":
&& 的短路:左边为假,直接返回左边,右边不执行。
false&&console.log("不会执行");// 返回 false,console.log 不会执行// 实用场景:条件执行isReady&&startApp();// isReady 为 true 时才执行 startApp()// 等价于if(isReady){startApp();}|| 的短路:左边为真,直接返回左边,右边不执行。
true||console.log("不会执行");// 返回 true,console.log 不会执行// 实用场景:设置默认值constuserName=inputName||"匿名用户";// inputName 为假值时,使用 "匿名用户"// 等价于constuserName=inputName?inputName:"匿名用户";4.3 逻辑运算符返回原始值
JavaScript 的逻辑运算符返回的是原始值,不是布尔值:
// && 返回第一个假值,或最后一个真值"hello"&&"world";// "world"(两边都是真值,返回最后一个)"hello"&&0;// 0(0 是假值,返回 0)null&&"world";// null(null 是假值,返回 null)// || 返回第一个真值,或最后一个假值"hello"||"world";// "hello"("hello" 是真值,直接返回)0||"world";// "world"(0 是假值,返回 "world")null||undefined;// undefined(都是假值,返回最后一个)实用技巧:
// 获取第一个真值constport=envPort||configPort||3000;// 条件赋值constdisplayName=user.nickname||user.name||"匿名";// 确保值存在后才执行user&&user.profile&&console.log(user.profile.bio);五、三元运算符
5.1 基本语法
条件?条件为真时的值:条件为假时的值constage=20;conststatus=age>=18?"成年":"未成年";console.log(status);// "成年"5.2 嵌套三元运算符
可以嵌套,但嵌套太多会降低可读性:
constscore=85;constgrade=score>=90?"A":score>=80?"B":score>=60?"C":"D";// 上面的代码等价于:letgrade;if(score>=90){grade="A";}elseif(score>=80){grade="B";}elseif(score>=60){grade="C";}else{grade="D";}建议:嵌套不要超过 2 层,否则用 if-else 更清晰。
六、ES6+ 新增运算符
6.1 空值合并运算符 ??(ES2020)
??只在左边是null或undefined时才返回右边,其他假值(0、""、false)不会触发。
// || 的问题:0 和 "" 会被当作假值constcount=0;constresult1=count||10;// 10(0 被当作假值,不符合预期)// ?? 只判断 null 和 undefinedconstresult2=count??10;// 0(0 不是 null/undefined,所以返回 0)// 更多示例null??"default";// "default"undefined??"default";// "default"0??"default";// 0""??"default";// ""false??"default";// false6.2 可选链运算符 ?.(ES2020)
安全地访问深层嵌套对象的属性,避免 “Cannot read property of undefined” 错误。
constuser={profile:{name:"小明"}};// 传统写法(繁琐)constcity=user&&user.address&&user.address.city;// 可选链写法(简洁)constcity=user?.address?.city;// undefined(不会报错)// 更多示例constname=user?.profile?.name;// "小明"constzip=user?.address?.zipcode;// undefined(不会报错)constphone=user?.contacts?.[0]?.phone;// undefined6.3 逻辑赋值运算符(ES2021)
| 运算符 | 示例 | 等效写法 |
|---|---|---|
| ` | =` | |
&&= | x &&= y | x = x && y |
??= | x ??= y | x = x ?? y |
// ||=:只在 x 为假值时赋值letconfig=null;config||={theme:"dark"};// config = { theme: "dark" }letcount=0;count||=10;// count 仍然是 0(因为 0 是假值,但 ||= 会把 0 覆盖)// ??=:只在 x 为 null/undefined 时赋值(更安全)letnum=0;num??=10;// num 仍然是 0(0 不是 null/undefined)七、位运算符(了解即可)
位运算符直接操作二进制位,日常开发中使用较少。
| 运算符 | 名称 | 示例 |
|---|---|---|
& | 按位与 | 5 & 3→1 |
| ` | ` | 按位或 |
^ | 按位异或 | 5 ^ 3→6 |
~ | 按位非 | ~5→-6 |
<< | 左移 | 5 << 1→10 |
>> | 右移 | 5 >> 1→2 |
>>> | 无符号右移 | -5 >>> 1→2147483645 |
实用技巧:用位运算取整
// 位运算取整(向下取整)~~3.14;// 33.14|0;// 33.14<<0;// 3// 判断奇偶num&1;// 0 表示偶数,1 表示奇数八、运算符优先级
当多个运算符同时出现时,优先级高的先计算。
8.1 优先级速查表(从高到低)
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 1 | () | 括号 |
| 2 | ++、-- | 自增、自减 |
| 3 | !、~、+(一元)、-(一元) | 逻辑非、按位非、正负号 |
| 4 | ** | 幂运算 |
| 5 | *、/、% | 乘、除、取余 |
| 6 | +、- | 加、减 |
| 7 | <<、>>、>>> | 位移 |
| 8 | <、<=、>、>= | 比较 |
| 9 | ===、!==、==、!= | 相等 |
| 10 | & | 按位与 |
| 11 | ^ | 按位异或 |
| 12 | ` | ` |
| 13 | && | 逻辑与 |
| 14 | ` | |
| 15 | ? : | 三元 |
| 16 | =、+=、-=等 | 赋值 |
| 17 | , | 逗号 |
8.2 优先级示例
// 不加括号2+3*4;// 14(先算 3*4=12,再算 2+12)// 加括号改变优先级(2+3)*4;// 20(先算 2+3=5,再算 5*4)// 复杂的优先级constresult=a+b>c*d&&e===f;// 实际执行顺序:// 1. c * d// 2. a + b// 3. (a + b) > (c * d)// 4. e === f// 5. (步骤3) && (步骤4)建议:不确定优先级时,加括号。可读性比省两个字符更重要。
九、常见误区与注意点
| 误区 | 正确理解 |
|---|---|
+总是做加法 | 有一边是字符串就拼接:"5" + 3 = "53" |
++i和i++没区别 | 在赋值时区别很大:a = i++vsa = ++i |
&&和 ` | |
??和 ` | |
?.可以无限链下去 | a?.b?.c,如果a是null,整个表达式返回undefined |
| 记住所有优先级 | 不用记,不确定就加括号 |
十、动手练习
练习 1:逻辑运算符输出
以下表达式返回什么?
"hello"&&"world";"hello"&&"";0||"default";null??"default";0??"default";""||"anonymous";""??"anonymous";答案"hello"&&"world";// "world"(两边都真,返回最后一个)"hello"&&"";// ""(空字符串是假值,返回它)0||"default";// "default"(0 是假值,返回右边)null??"default";// "default"(null 触发 ??)0??"default";// 0(0 不是 null/undefined,不触发 ??)""||"anonymous";// "anonymous"(空字符串是假值,触发 ||)""??"anonymous";// ""(空字符串不是 null/undefined,不触发 ??)练习 2:用三元运算符简化代码
将以下 if-else 改写成三元运算符:
letresult;if(score>=60){result="及格";}else{result="不及格";}参考答案constresult=score>=60?"及格":"不及格";练习 3:用可选链安全访问
安全地获取user.profile.address.city,避免报错:
constuser={profile:{// address 可能不存在}};参考答案// 传统写法constcity=user&&user.profile&&user.profile.address&&user.profile.address.city;// 可选链写法constcity=user?.profile?.address?.city;// 配合 ?? 设置默认值constcity=user?.profile?.address?.city??"未知城市";十一、AI 辅助学习
11.1 本节知识点的 AI 提问模板
【背景】我是 JavaScript 初学者,正在学习第 09 篇"运算符大全"。 我已经了解算术、比较、逻辑运算符的基本用法。 【问题】我理解短路求值的概念,但我不理解: 为什么 JavaScript 设计者要让 && 和 || 返回原始值而不是布尔值? 这不会导致类型混乱吗? 【期望】请解释 && 和 || 返回原始值的设计动机, 列举 3 个因为这种设计而变得简洁的代码场景, 最后说明在使用时如何避免类型相关的 bug。11.2 用 AI 验证你的理解
- 问 AI:“
a ?? b || c这个表达式,运算符优先级是怎样的?先算哪个?” - 让 AI 出 3 道关于运算符优先级的题目,测试是否需要加括号
- 问 AI:“
?.和??可以组合使用吗?给一个实际开发中的例子。”
11.3 警惕 AI 的常见错误
- AI 有时会混淆
??和||的行为差异,注意验证 - AI 可能会给出优先级错误的表达式,不确定时自己加括号
- AI 可能会推荐已废弃的写法(如用
==做比较),坚持使用===
十二、配套代码
本文示例代码位于:CODE/09-运算符/
| 文件名 | 说明 |
|---|---|
operator-playground.html | 运算符练习场:可视化展示各种运算符的计算过程和结果 |
十三、本章小结
- 算术运算符:
+ - * / % **,注意+的字符串拼接特性 - 自增自减:
++--,前置和后置在赋值时有区别 - 比较运算符:始终用
===和!== - 逻辑运算符:
&&||!,支持短路求值,返回原始值 - 三元运算符:
条件 ? 值1 : 值2,嵌套不要超过 2 层 ??:只在null/undefined时取默认值,比||更精确?.:安全访问深层属性,避免 undefined 报错- 优先级:不确定就加括号,可读性第一
十四、下篇预告
下一篇是《流程控制:if、switch、循环结构》,你将学到:
- if/else if/else 的条件判断
- switch case 的多分支选择
- for、while、do…while 循环
- break 和 continue 的使用
- 嵌套循环和循环优化
如果本文对你有帮助,欢迎点赞、收藏、关注专栏。有任何问题可以在评论区交流!
