- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
本文以 Modern JavaScript Tutorial(en.javascript.info 仓库)中1-js/02-first-steps/08-operators/1-increment-order一节的练习题及其官方解答为核心,系统讲解 JavaScript 中++/--的前置与后置两种形式在返回值上的本质差异,并结合同章节正文(article.md)的运算符优先级、赋值返回值、就地修改等知识,帮助你彻底理清「求值顺序」这一经典易错点,做到在任何表达式中都能准确推演出最终结果。
题目回顾:The postfix and prefix forms
在仓库 task.md 中,题目给出如下代码,要求判断变量a、b、c、d在代码执行完毕后的最终值:
let a = 1, b = 1; let c = ++a; // ? let d = b++; // ?题目本身只有四行代码,但它精准地命中了一个几乎每个 JavaScript 初学者都会踩的坑:同是自增,写在变量前面和写在变量后面,赋值结果截然不同。这道题在题目标注中的importance: 5,也说明它是教程作者认定的高优先级核心知识点。
官方答案与逐步推导
仓库中的官方解答 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 a = 1, b = 1;——a和b初始值都是1。let c = ++a;——++a是前置形式:先把a加1变成2,然后把这个新值2作为表达式结果返回,赋给c。因此a = 2,c = 2。let d = b++;——b++是后置形式:先把b的旧值1作为表达式结果返回,赋给d,然后才把b加1变成2。因此d = 1,b = 2。
可以看到:无论前置还是后置,变量本身最终都会被加 1(a、b最终都是2);区别只在于表达式返回给调用方的值——前置返回新值,后置返回旧值。这正是c与d相差 1 的根本原因。
前置与后置:什么时候用哪一种
教程正文 article.md 的 "Increment/decrement" 小节对两种形式的选用场景给出了明确指引:
自增/自减的结果不被使用时,两种形式没有区别:
let counter = 0; counter++; ++counter; alert( counter ); // 2, the lines above did the same(上面两行效果一样)想先增加值、并立刻使用运算结果,用前置形式:
let counter = 0; alert( ++counter ); // 1想增加值、但需要使用它增加之前的值,用后置形式:
let counter = 0; alert( counter++ ); // 0
一句话总结记忆口诀:前置「先加后用」,后置「先用后加」。绝大多数日常代码(如for循环的i++、计数器累加)都不使用返回值,此时随便选哪种写法结果都一致,可读性优先。
深层原理一:所有运算符都会返回一个值
为什么前置/后置的差异如此重要?教程正文在 "Assignment = returns a value" 一节中指出:JavaScript 中所有运算符都会返回一个值,=也不例外——x = value在把value写入x之后,整个表达式还会返回这个值。
let a = 1; let b = 2; let c = 3 - (a = b + 1); // 括号内赋值表达式返回 3 alert( a ); // 3 alert( c ); // 0++/--同样遵循这一规则:它既改变变量的值,又向外返回一个值。前置与后置的差别,正是这个「返回值」的内容不同(新值 vs 旧值)。理解了「运算符即表达式、表达式必有值」这条语言底层规则,let c = ++a;这类写法就一点也不神秘了。
深层原理二:优先级决定求值顺序
教程正文的 "Operator precedence" 一节给出了 JavaScript 运算符的优先级表摘录:
| Precedence(优先级) | Name(名称) | Sign(符号) |
|---|---|---|
| ... | ... | ... |
| 14 | unary plus(一元加) | + |
| 14 | unary negation(一元负) | - |
| 13 | exponentiation(幂) | ** |
| 12 | multiplication(乘) | * |
| 12 | division(除) | / |
| 11 | addition(加) | + |
| 11 | subtraction(减) | - |
| ... | ... | ... |
| 2 | assignment(赋值) | = |
| ... | ... | ... |
优先级数字越大,越先执行;同级运算符从左到右执行。教程正文明确指出:++/--的优先级高于绝大多数算术运算符。这一特性如果叠加在前置/后置差异上,就构成了一个经典的「组合陷阱」:
let counter = 1; alert( 2 * ++counter ); // 4这里++counter先执行:counter变成2,返回新值2,再乘以2,结果是4。
let counter = 1; alert( 2 * counter++ ); // 2, because counter++ returns the "old" value这里虽然counter++同样优先执行,但它返回的是旧值1,所以2 * 1结果是2。两段代码中counter的最终值都是2,但表达式整体结果一个是4、一个是2——这正是前文题目中c与d差异在复合表达式里的放大版。
再结合优先级表理解++a的执行时机:自增属于高优先级(一元)运算,会先于乘除、加减完成;而赋值=的优先级极低(仅2),所以在let c = ++a;中,++a一定先于=执行,c拿到的是自增后的返回值。
结合仓库其他练习题:同一个知识点的不同考法
理解了前置/后置与「赋值也返回值」的规则后,你会发现仓库中同章节的其他题目其实都在考察同一套底层机制:
练习二 "Assignment result"(task.md):
let a = 2; let x = 1 + (a *= 2);官方解答(solution.md):a = 4(乘以 2),x = 5(按1 + 4计算)。它考察的正是「就地修改运算符*=也是表达式、会返回赋值结果」——与++a返回新值是同一条规则的不同体现。
练习一正是本篇文章的题源,它与练习二共同构成一组「自增/赋值返回值」的组合训练。做完这两题,你对「表达式求值顺序」的理解就能串成一条完整的知识链。
实战建议:避免在复合表达式中使用自增
虽然2 * counter++在语法上完全合法,但教程正文给出了明确的工程建议:不要让一行代码做多件事,这类写法通常降低可读性——快速纵向扫视代码时,counter++很容易被忽略,变量的变化不易被察觉。
推荐的写法是「一行一个动作」:
let counter = 1; alert( 2 * counter ); counter++;同理,教程正文还提醒:虽然a = b = c = 2 + 2这类链式赋值语法可用(从右向左求值),但为了可读性,最好拆成多行。这些建议背后是同一个原则:优先保证代码意图的清晰,而不是炫技。
小结
回到本篇文章的题源练习,最终结论再强化一遍:
| 变量 | 初始值 | 执行语句 | 最终值 | 原因 |
|---|---|---|---|---|
a | 1 | ++a | 2 | 前置形式先自增 |
b | 1 | b++ | 2 | 后置形式最终同样自增 |
c | — | c = ++a | 2 | 前置返回新值 |
d | — | d = b++ | 1 | 后置返回旧值 |
掌握这条规则需要记住三个核心事实:
++/--无论前后,最终都会让变量加/减 1;- 前置形式返回新值,后置形式返回旧值——差异只体现在表达式的结果上;
++/--优先级高于绝大多数算术运算符,而赋值=优先级很低,因此在let c = ++a;中自增必然先于赋值完成。
想动手验证,可以直接在浏览器控制台或任意 Node.js 环境运行 solution.md 中的示例代码;想要继续巩固,可以接着完成同章节的 Assignment result、Type conversions 与 Fix the addition 三组练习,把运算符、类型转换与求值顺序的知识一次打通。
- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
相关推荐
30-seconds-of-interviews 面试题精讲:postfix `i++` 与 prefix `++i` 自增运算符到底有什么区别
30 seconds of interviews 面试题精讲:postfix i++ 与 prefix ++i 自增运算符到底有什么区别 本篇技术指南以 30
教程前端PHP-CS-Fixer `increment_style` 规则详解:统一自增自减运算符的前置与后置风格
PHP CS Fixer increment_style 规则详解:统一自增自减运算符的前置与后置风格 自增( ++ )与自减( )运算符的前置(pre)与后置
开发工具代码质量静态分析Lint格式化提升开源贡献成功率:contribute-to-open-source代码规范与测试指南
提升开源贡献成功率:contribute to open source代码规范与测试指南 GitHub 加速计划(contribute to open sour
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考