- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
本文以 Modern JavaScript Tutorial(en.javascript.info仓库)中 基础运算符与数学运算 章节的配套练习 The postfix and prefix forms 及其 官方解答 为主体,深入剖析 JavaScript 中++/--运算符的前缀形式与后缀形式在"返回值"上的本质区别,并在此基础上延伸到表达式求值顺序、运算符优先级与代码可读性等实战要点。读完本文,你将能准确预测任何含自增自减表达式的结果,并写出可读性更佳的代码。
一、练习题目:四个变量的最终值
仓库中 task.md 给出了一道经典入门题(重要性标记为importance: 5,属于最高优先级的基础练习):
let a = 1, b = 1; let c = ++a; // ? let d = b++; // ?请判断执行完毕后变量a、b、c、d分别等于多少。这道题的价值在于:它同时考察了赋值语句的执行顺序、自增运算符的副作用以及前缀/后缀形式的返回值差异三个知识点。
二、官方解答与逐步推演
根据仓库中的 solution.md,最终答案是:
a = 2b = 2c = 2d = 1
官方解答用一段可直接运行的代码验证了这一点:
let a = 1, b = 1; alert( ++a ); // 2, prefix form returns the new value alert( b++ ); // 1, postfix form returns the old value alert( a ); // 2, incremented once alert( b ); // 2, incremented once逐步推演如下:
let c = ++a;:这是前缀形式。运算符先对a执行a = a + 1(此时a变为2),随后把新值2作为表达式结果返回并赋给c。因此a = 2、c = 2。let d = b++;:这是后缀形式。运算符先把b的旧值1作为表达式结果返回并赋给d,随后才执行b = b + 1(此时b变为2)。因此b = 2、d = 1。
注意两个易错点:第一,无论前缀还是后缀,a和b都确实被自增了一次,所以它们最终都是2;第二,二者差异只体现在"表达式本身返回的值"上——前缀返回新值,后缀返回旧值。
三、深入原理:为什么前缀返回新值、后缀返回旧值
章节正文 article.md 在 "Increment/decrement" 一节中给出了完整理论:
- 增量
++使变量增加1,减量--使变量减少1,它们等价于counter = counter + 1/counter = counter - 1的简写; - 运算符可以放在变量之前(前缀形式
++counter)或变量之后(后缀形式counter++); - 两种形式对变量本身的修改完全相同,唯一区别在于表达式返回的值:前缀形式返回自增/自减之后的新值,后缀形式返回自增/自减之前的旧值。
正文通过两个对照例子直观说明了这一点:
let counter = 1; let a = ++counter; // 前缀形式:counter 先变 2,再返回 2 alert(a); // 2let counter = 1; let a = counter++; // 后缀形式:先返回旧值 1,counter 再变 2 alert(a); // 13.1 理解"所有运算符都返回值"这一前提
要彻底理解上述差异,需要先建立正文中强调的一个观念:JavaScript 中所有运算符都会返回一个值。+、-如此,=赋值运算符如此,++/--也不例外。正文专门讨论了赋值运算符=的返回值特性(见 "Assignment = returns a value" 小节):x = value在把value写入x的同时会返回该值,因此它可以作为更大表达式的一部分:
let a = 1; let b = 2; let c = 3 - (a = b + 1); // (a = b + 1) 的结果是 3 alert( a ); // 3 alert( c ); // 0同理,++/--在执行副作用(修改变量)的同时也会产生一个返回值,前缀与后缀的差异正是"返回值取自修改前还是修改后"。
3.2 一个形象的记忆方法
可以把后缀形式理解为"先取票、后上车":先把当前值取出来作为表达式结果,再执行自增;前缀形式则是"先上车、后取票":先完成自增,再把新值作为表达式结果。只要分清"表达式结果"与"变量最终值"是两回事,这类题目就不会再出错。
四、实战场景与易错点扩展
4.1 后缀形式在循环中的经典误用
最常见的使用场景是for循环的更新语句。仓库正文在 "Comma" 一节给出过循环示例:
for (a = 1, b = 3, c = a * b; a < 10; a++) { // ... }这里的a++单独作为语句使用,结果被丢弃,因此前缀/后缀没有区别。但如果把a++放进更复杂的表达式,差异立刻显现。正文 "Increment/decrement among other operators" 的示例:
let counter = 1; alert( 2 * ++counter ); // 4,counter 先变 2,再乘以 2let counter = 1; alert( 2 * counter++ ); // 2,先取旧值 1 乘以 2,counter 随后变 2同样的两行代码,仅仅因为++的位置不同,输出就从4变成了2。这正是++/--出现在表达式中时最容易被忽略的陷阱。
4.2 优先级视角:自增自减高于绝大多数算术运算
正文的运算符优先级表(摘录)显示,自增/自减与前缀一元运算的优先级很高:
| 优先级 | 名称 | 符号 |
|---|---|---|
| 14 | 一元加 | + |
| 14 | 一元负 | - |
| 13 | 幂 | ** |
| 12 | 乘 | * |
| 12 | 除 | / |
| 11 | 加 | + |
| 11 | 减 | - |
| 2 | 赋值 | = |
正文指出:自增/自减的优先级高于大多数其他算术运算,因此2 * ++counter会先计算++counter再执行乘法;而赋值运算符=优先级极低(2),所以let c = ++a;一定是先求值完右侧表达式,再把结果赋给c——这与本练习的推演逻辑完全一致。结合同目录另一道练习 Assignment result 可以进一步体会"赋值会返回被赋的值"这一机制:
let a = 2; let x = 1 + (a *= 2); // a 变为 4,x = 1 + 4 = 54.3 约束:只能作用于变量
正文给出了一个重要警告:自增/自减只能应用于变量,试图对字面量使用如5++会直接报错。因为该运算符需要"原地修改"一个可写的位置,而不是产生一个新值。
4.4 三个结论速查
正文在 "Increment/decrement" 小节末尾总结了三种情形,可直接作为速查表:
- 不使用返回值:前缀与后缀没有区别(
counter++;与++counter;效果相同); - 需要自增后的新值:必须用前缀形式,如
alert( ++counter ); // 1; - 需要自增前的旧值:必须用后缀形式,如
alert( counter++ ); // 0。
五、编码规范建议:一行只做一件事
正文对该主题给出的最终建议值得单独强调:虽然把++/--嵌入复杂表达式在技术上完全合法,但通常会让代码可读性下降。一行代码同时完成"取值、自增、参与运算"多件事,快速纵向扫读时很容易漏看counter++这样的副作用。正文推荐的风格是"一行一个动作":
let counter = 1; alert( 2 * counter ); // 先使用当前值 counter++; // 再单独自增同样地,赋值链a = b = c = 2 + 2虽然合法(从右向左依次赋值),正文也建议拆成多行:
c = 2 + 2; b = c; a = c;这种"显式优于隐式"的写法更利于快速扫读与后续维护——毕竟本题(前缀/后缀差异)存在的根源,就是隐式副作用带来的不确定性。
六、小结与延伸练习
本练习的最终答案:a = 2、b = 2、c = 2、d = 1。核心结论一句话概括:前缀形式++x返回自增后的新值,后缀形式x++返回自增前的旧值,但两种形式都会让变量本身加 1。
如果你希望进一步巩固本主题相关的知识,可以继续阅读:
- 运算符与数学计算的完整章节:article.md(覆盖字符串拼接、一元/二元运算符、优先级表、赋值返回值、逗号运算符等相邻主题);
- 同目录关联练习:Assignment result(考察赋值表达式返回值)与 Primitive conversions questions(考察类型转换在运算符中的表现);
- 整个基础语法阶段的学习路径:JavaScript Fundamentals 目录,可按照变量、类型、运算符、控制流的顺序系统掌握。
- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
相关推荐
Modern JavaScript 教程精讲:前置(prefix)与后置(postfix)自增/自减运算符深入解析
Modern JavaScript 教程精讲:前置(prefix)与后置(postfix)自增/自减运算符深入解析 本文以 Modern JavaScript
文档/教程前端LeetCode 2483 店铺最小罚款(Minimum Penalty for a Shop):前缀后缀统计与四类扫描解法精讲
LeetCode 2483 店铺最小罚款(Minimum Penalty for a Shop):前缀后缀统计与四类扫描解法精讲 本指南围绕 LeetCode
示例工程教程JavaScript 变量实战:声明、赋值与传递 —— Modern JavaScript Tutorial 练习详解
JavaScript 变量实战:声明、赋值与传递 —— Modern JavaScript Tutorial 练习详解 本篇指南围绕 Modern JavaSc
文档/教程前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考