拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Modern JavaScript Tutorial 练习精讲:前缀与后缀自增自减(`++a` 与 `a++`)的求值差异

Modern JavaScript Tutorial 练习精讲:前缀与后缀自增自减(`++a` 与 `a++`)的求值差异
  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载

本文以 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 = 2
  • b = 2
  • c = 2
  • d = 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

逐步推演如下:

  1. let c = ++a;:这是前缀形式。运算符先对a执行a = a + 1(此时a变为2),随后把新值2作为表达式结果返回并赋给c。因此a = 2、c = 2。
  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); // 2
let counter = 1; let a = counter++; // 后缀形式:先返回旧值 1,counter 再变 2 alert(a); // 1

3.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,再乘以 2
let 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 = 5

4.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

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载
上一篇:UnifiedBus超节点:革命性异构硬件统一抽象平台终极指南
下一篇:Formily React 指南:@formily/react 的架构、模型绑定与三种开发模式

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表