docs(js_notes): complete ch09 loops exercises + add semicolons/ASI note

- 09-loops: finish exercises A-F
- 00-concepts: add semicolons-and-asi.md (when ; is mandatory: line
  starting with ( or [, no newline after return/throw)
- README: index the new concept note
pull/1034/head
Jaydon 1 week ago
parent e9fb78c3f3
commit a2e55346a5

@ -0,0 +1,81 @@
# 通用概念 · 分号 `;`:什么时候【必须】写
> JS/TS 每句结尾的 `;` 绝大多数可省(引擎会自动补),但**有几种情况不写就是坑**。
> 本篇只记"**必须写、不写就出 bug**"的场景。相关:`../09-loops.js`(无分号风格)。
## 背景:ASI —— 引擎会自动补分号
JS 有个机制叫 **ASI(Automatic Semicolon Insertion,自动分号插入)**:
你不写 `;`,引擎在换行处**通常**会替你补上。所以这两种写法行为完全一样:
```js
const a = 1; // 写了
const a = 1 // 没写,ASI 自动补 —— 等价
```
> 本仓库 `js_notes/` 统一用**无分号**风格(Prettier 默认),保持一致即可,别混着来。
> 但无分号的前提是:**知道下面这几个 ASI 会猜错的地方,主动兜底。**
## ⚠️ 坑 1:下一行以 `(``[` 开头 → 必须在【行首】补 `;`
ASI 看到下一行还能和上一行"连成一句",就**不补**分号,于是两行被黏成一句:
```js
// 💥 出错:ASI 不补,被理解成 5[1,2,3]... 或 b(...)
const x = 5
[1, 2, 3].forEach((n) => console.log(n)) // TypeError: Cannot read properties...
const b = foo
(function () {})() // foo(function(){})() —— 把 foo 当函数调用
```
**正解:在以 `(` / `[` 开头的那行,行首加 `;`**(不是上一行结尾):
```js
const x = 5
;[1, 2, 3].forEach((n) => console.log(n)) // ✅ 行首 ; 把两句切开
```
口诀:**一行别以 `(``[` 开头;非要这么写,行首先放一个 `;`。**
(常见于:上一行是赋值/表达式,下一行想直接 `[...]`.map() 或 `(...)` 立即执行。)
## ⚠️ 坑 2:`return` / `throw` 等后面【不能】换行
这几个关键字后面一旦换行,ASI 会**强制**在关键字后补 `;`,把真正想返回的东西丢掉:
```js
// 💥 等于 return; 换行后的对象被当成"死代码"丢弃 → 函数返回 undefined
function make() {
return
{ name: 'Tom' }
}
make() // undefined,不是那个对象!
```
**正解:要返回的东西和 `return` 写在同一行**(对象太长就用括号包起来接着写):
```js
function make() {
return { name: 'Tom' } // ✅ 同一行
}
```
同样受影响的:`return` / `throw` / `break` / `continue` / `yield` —— **后面别换行**。
## ⚠️ 坑 3(顺带):同一行写两句,中间【必须】有 `;`
分号真正的本职工作是"分隔语句"。想把两句挤在一行,中间**必须**加 `;`:
```js
let a = 1 let b = 2 // 💥 语法错误
let a = 1; let b = 2 // ✅ 一行两句,靠 ; 分隔
```
## ✅ 一句话
> 平时跟着仓库的**无分号**风格写就行,ASI 帮你兜底。只死记两条保命规则:
> 1. **行首是 `(``[`** → 这一行**行首**补 `;`;
> 2. **`return`/`throw`/`break`/`continue` 后面不换行**(要返回的写同一行)。
>
> 装了 Prettier 的话,保存时会自动帮你处理坑 1 的行首 `;`,更省心。
> TS 规则完全一样(TS 是 JS 超集),只是 TS 项目社区**更偏爱每句都写 `;`** —— 那是团队约定,不是语法强制。

@ -96,22 +96,62 @@ for (let i = 0; i < 5; i++) {
// (只给要求,自己写代码)
// 练习 A:用 for 循环打印 1 到 5(注意:是 1 到 5,不是 0 到 4)
for (let i = 1; i <= 5; i++) {
console.log(i)
}
// 练习 B:有 const nums = [4, 7, 2, 9]
// 用 for...of 遍历,把每个数字打印出来
const nums = [4, 7, 2, 9]
for (const num of nums) {
console.log(num)
}
// 练习 C:有 const car = { brand: 'Tesla', year: 2024, color: 'red' }
// 用 for...in 遍历,按 "键: 值" 的格式打印每一项
// (例如 brand: Tesla)
const car = { brand: 'Tesla', year: 2024, color: 'red' }
for (const i in car) {
console.log(`${i}` + ": " + car[i]) // ✅
console.log(i + ": ", car[i]) // ❌
console.log(i + ":", car[i]) // ✅
}
// 练习 D:用 for 循环算出 1 + 2 + 3 + ... + 100 的总和,打印结果
// 提示:在循环外先定义一个 let sum = 0,每轮把 i 加进去
// (答案应该是 5050)
let sum = 0
for (let i = 1; i <= 100; i++) {
sum += i
}
console.log(sum)
// 练习 E(动脑):有 const nums = [3, 8, 1, 10, 5]
// 用循环找出里面【最大】的数并打印。
// 提示:先假设第一个是最大(let max = nums[0]),
// 遍历时遇到更大的就更新 max。
const nums_ = [3, 8, 1, 10, 5]
let max = nums_[0]
for (const num of nums_) {
max = num > max? num : max
}
console.log(max)
// 练习 F(挑战):打印 1 到 20,但遇到偶数就跳过(用 continue),
// 只打印奇数。
for (let i = 1; i <= 20; i+=1) {
if (i % 2 == 0) // ❌
if (i % 2 === 0) // ✅
continue // ❌
{continue} // ✅
console.log(i)
}

@ -39,6 +39,7 @@
- `00-concepts/call-forms.md` —— 三种调用形式:`值.方法()` vs `函数()` vs `类.方法()`
- `00-concepts/value-vs-reference.md` —— 值 vs 引用:复制 vs 共享、字符串不可变 ⭐
- `00-concepts/shortcircuit-bash-vs-js.md` —— `&&`/`||`:bash vs JS(`0` 真假相反)
- `00-concepts/semicolons-and-asi.md` —— 分号 `;` 什么时候【必须】写(ASI 的坑:行首 `(`/`[`、`return` 换行)⭐
### 一、基础与数据
- [x] `01-basics` 基础:输出、注释、表达式、字符串引号、运行方式

Loading…
Cancel
Save