Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7b9ddf1
docs(js_notes): add learning notes 01-09 in single-file format
JaydonZhao Jul 18, 2026
e9fb78c
docs(js_notes): fix factual errors and imprecise wording
JaydonZhao Jul 18, 2026
a2e5534
docs(js_notes): complete ch09 loops exercises + add semicolons/ASI note
JaydonZhao Jul 19, 2026
11568b3
docs: update CLAUDE.md for flat structure + learning-trace convention
JaydonZhao Jul 19, 2026
3e57a6a
docs(js_notes): adapt roadmap toward pi (agent-oriented path)
JaydonZhao Jul 19, 2026
fe99e70
docs(claude): add tutoring rule — explain the why, anchor-first
JaydonZhao Jul 22, 2026
b16580c
docs(js_notes): add ch10 functions + label statement + mistakes N/O
JaydonZhao Jul 22, 2026
b574850
docs: translate CLAUDE.md to Chinese
JaydonZhao Jul 22, 2026
b4bb2f0
docs(claude): add 'write explanations into files' rule; reword 锚点
JaydonZhao Jul 23, 2026
25be1a4
docs(js_notes): explain hoisting + TDZ in ch10; reword 锚点
JaydonZhao Jul 23, 2026
8491827
docs(js_notes): deepen type concepts (null, object essence)
JaydonZhao Jul 23, 2026
a740a95
docs(claude): fix STT typo 活着->或者 in mistakes-log rule
JaydonZhao Jul 23, 2026
475671f
docs(js_notes): complete ch10 functions (exercises + name-vs-var note)
JaydonZhao Jul 23, 2026
13bc50e
docs(js_notes): keep improved repeat (edge case), comment out old ver…
JaydonZhao Jul 23, 2026
576a619
docs(claude): add per-chapter wrap-up checklist rule
JaydonZhao Jul 23, 2026
0e16281
docs(js_notes): deepen ch10 — functions are objects, fix experiment t…
JaydonZhao Jul 24, 2026
f672004
docs(js_notes): add expression-vs-statement concept note
JaydonZhao Jul 24, 2026
edfff2a
docs(concepts): note JS &&/|| only chains expressions, unlike shell
JaydonZhao Jul 24, 2026
a0f8e53
docs(js_notes): complete ch11 higher-order functions (map/filter/reduce)
JaydonZhao Jul 25, 2026
c4e5d68
chore(skills): add learn-tidy skill for tidying practice code
JaydonZhao Jul 25, 2026
0c830c9
docs(day11): fix typos in destructuring chapter
JaydonZhao Jul 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions .claude/skills/learn-tidy/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: learn-tidy
description: >
整理用户在 js_notes/ 学习练习里刚写完或改完的代码 —— 跑真实输出、把错法整理成
❌/✅ 对照痕迹、修正写错的结果注释、把有教益的新错误吸收进 my-mistakes.md。
当用户说「整理」「整理一下」「tidy」「收拾干净」「帮我整理这段/这题」,或刚做完一章练习、
刚改完一段试验代码、留下了对错混杂的多版尝试时,主动用这个 skill。它是这个学习仓库
反复要做的收尾动作,专门服务 js_notes/ 的教学约定,别用内置 code-review 代替。
---

# learn-tidy · 整理学习练习代码

把用户刚写/改的练习或试验代码"收拾干净",让它成为将来复习时**看得懂、可追溯、能跑**的
学习痕迹。这是本仓库(30-Days-Of-JavaScript)`js_notes/` 反复要做的收尾动作。

先读 `CLAUDE.md` 与 `js_notes/README.md` 里的辅导规则(它们是**具有约束力的**),本 skill
是那套规则在"整理"这个动作上的具体落地,不得与之冲突。

## 什么时候用

- 用户说「整理」「整理一下 / 这段 / 这题」「tidy」「收拾干净」
- 用户刚做完一章练习(A–F 之类)、或刚改完一段 `// 试验 / 探索` 代码
- 文件里留下了**对错混杂的多版尝试**(比如 3 行 reduce、2 个同名 const)

## 核心原则(为什么这么做)

成品是**文件**,不是聊天:将来的用户只会翻文件,不会翻对话记录。所以整理的目标是让文件
**自解释**——一眼看到"错在哪、为什么错、正解是什么"。而且**绝不留能跑的坏代码**:错的要么
删、要么注释成对照,活代码必须是对的。

## 流程(按顺序做,别跳步)

### 1. 先读,再跑真实输出(绝不凭记忆)

- Read 目标文件相关段落,搞清哪些是用户的练习答案、哪些是试验、哪些是还没动的「轮到你」。
- **跑 `node js_notes/NN-topic.js`** 拿到**真实输出**。需要只查语法时先 `node --check`。
- 临时验证可写 `/tmp/*.js` 跑,但**结论必须落进正式文件**,别把只存在于 /tmp 或聊天里的
结果当交付。
- 用真实输出对照用户写的"预期注释"——**注释写错的结果要改对**(这是高频问题,例如用户
写 `// "修改y的name"` 但实跑是 `fn`)。

### 2. 把错法整理成 ❌ / ✅ 对照(留学习痕迹)

对用户犯过的错、或对错混杂的多版尝试:

- **错的那版注释掉**(绝不留能跑的坏代码),放在**正解上方**。
- 加一行简短 `// ❌ 原因` 说清*为什么*错;正解用 `// ✅` 标出、留作**活代码**。
- 多版尝试(如 3 行 reduce)收敛成:错的都注释成 ❌ 对照,最优的一版留活代码。
- 若某版"能救回来但不优雅",可用 `// 🔸` 标注中间方案,但活代码留最干净的那版。

风格对齐用户已在 `06-arrays.js` / `07-objects.js` / `10-functions.js` 用的写法:

```js
// ❌ ^ 在 JS 是"按位异或"不是乘方:8^2=10、和平方无关
// console.log(nums.filter(n => n % 2 === 0).map(n => n^2))
// ✅ 乘方用 **(或 n*n)
console.log(nums.filter(n => n % 2 === 0).map(n => n ** 2)) // [ 64, 100 ]
```

### 3. 决定要不要吸收进 my-mistakes.md(关键判断,别做流水账)

**不是所有错误都记。** `my-mistakes.md` 是"复习时值得回看的坑",不是错误日志。三档处理:

| 情况 | 怎么办 |
|---|---|
| 新的、有教益的坑(尤其"能跑不报错"的概念坑) | ✅ 新开一条 |
| 已有条目的重演 / 变体 | 🔸 不新开,在原条目**补一句**新情境 |
| 纯手误 / 已被现有条目覆盖 / 用户已自己改好且无新东西可学 | ❌ 不记 |

新开条目沿用现有格式:**错法 → 现象 → 正解/根源 → 教训**,并带 `📍来源`(文件 + 练习号)。
能跑不报错的坑标 ⭐,概念性推错(非手误)可标 ⭐⭐ 并注明"概念推错,非手滑"。若坑对应某个
报错信息,顺手更新末尾的「高频报错 → 秒查表」。

**拿不准值不值得记时,问用户。**(用户明确说过:记流水账他会反驳。)

### 4. 不碰用户还没做的练习

「轮到你」里用户**还没动手**的题,**绝不**往里写解答(哪怕注释掉也不行,会剧透)。整理只针对
用户**已经写了**的答案/试验。

### 5. 跑一遍验证 + 收尾

- 改完后再 `node js_notes/NN-topic.js`(或 `node --check`)确认整章能跑、输出符合预期。
- 如果这次整理正好是"一章练习全部做完",可按 `CLAUDE.md` 的「一章收尾必做清单」把 README
进度勾选也带上。

### 6. commit —— 灵活判断,别默认执行

**不要每次整理都自动 commit。** 由具体情形决定:

- 若这次整理是一个**逻辑完整的收尾**(一章做完、或一段独立讲解落定),且工作区就这些改动
→ 可以提议 commit,并给出 Angular 风格 message(`docs(js_notes): ...`),**先说清要提交什么**。
- 若还在**中途**(用户可能马上继续改)、或工作区混着其它未完成改动 → **先别 commit**,
只把整理做好、告诉用户"整理完了,要提交时说一声"。
- 拿不准时,问用户要不要提交,而不是擅自 commit。

commit 前遵守 `CLAUDE.md` 的 Git 约定:干净拆分、绝不提交私密内容、不丢弃用户工作。

## 结束时给用户的话

简短汇报:跑出来的真实输出、留了哪些 ❌/✅ 痕迹、有没有(以及为什么)动 my-mistakes、
要不要 commit。别长篇复述文件内容——用户会自己翻文件。
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ console.log(backEnd)
["Node", "Express", "MongoDB"]
```

If we like to skip on of the values in the array we use additional comma. The comma helps to omit the value at that specific index
If we like to skip one of the values in the array we use additional comma. The comma helps to omit the value at that specific index

```js
const numbers = [1, 2, 3]
Expand Down Expand Up @@ -184,7 +184,7 @@ Node Express MongoDB

### Destructuring Object

When we destructure the name of the variable we use to destructure should be exactly the same as the key or property of the object. See the example below.
When we destructure, the name of the variable we use to destructure should be exactly the same as the key or property of the object. See the example below.

```js
const rectangle = {
Expand Down Expand Up @@ -258,7 +258,6 @@ const calculatePerimeter = rectangle => {
}

console.log(calculatePerimeter(rect)) // 60
//with destructuring
```

```js
Expand Down
169 changes: 169 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# CLAUDE.md

本文件为 Claude Code (claude.ai/code) 在本仓库中工作时提供指引。

## 这个仓库到底是什么

这里并存着两层内容:

1. **上游教程** —— `NN_Day_*` 文件夹(例如 `01_Day_Introduction`、
`23_Day_Event_listeners`),以及各语言翻译文件夹(`Korea/`、`RU/`、
`Spanish/`……)。这是 Asabeneh 的 "30 Days Of JavaScript" 课程,作为**只读**
参考资料克隆进来。**不要修改或"修复"这些文件** —— 其中的错误(例如 `main.js`
依赖另一个单独加载的 `variable.js` 里的变量)是刻意为之的教学产物,不是 bug。

2. **`js_notes/`** —— 用户自己的学习笔记,也是**活跃工作区**。
几乎所有真正的工作都发生在这里。它**按知识主题组织,而非按天**
(`01-basics`、`02-variables`、……`07-objects`……),因为这个编号反映的是
用户的学习顺序,而不是教程的天数。

用户是**正在入门 JavaScript 的初学者**(有一些 Python/Linux 背景)。
每次会话都是交互式辅导:讲一个主题、让用户练习、复审他的代码。

## `js_notes/` 的结构与约定

- **一个主题一个文件**,平铺在 `js_notes/` 根目录:`NN-topic.js`(例如
`09-loops.js`)。每个文件都自成一体,包含"学习 + 练习 + 复习",分为三个
带标签的区块,顺序如下:
1. `// ═════ 概念总结 ═════` —— 以注释形式呈现概念(含对齐的 ASCII 表格)
2. `// ═════ 示范 ═════` —— 可运行的演示代码,可带预期输出注释
3. `// ═════ 轮到你 ═════` —— 练习:只给要求,由用户自己写代码
`js_notes/README.md` 是索引 + 进度地图。
- **历史沿革:** 主题过去是 `NN-topic/` 文件夹,里面放 `notes.md` +
`practice.js`;后来被扁平化为单个 `NN-topic.js` 文件。`my-mistakes.md` 里有些
`📍来源` 链接仍指向旧的 `../NN-topic/practice.js` 路径 —— 请把它们视作
`../NN-topic.js`(只在你本来就在编辑那条条目时才顺手修链接;不要对用户的文件
做一次性的全量重写)。
- `js_notes/00-concepts/` 保持为一个**文件夹**,存放跨主题的 `.md` 笔记
(值 vs 引用、调用形式、bash-vs-js 短路、分号与 ASI),外加
**`my-mistakes.md`** —— 一份用户实际犯过的错误的滚动记录,每条采用
错法→现象→正解→教训 格式,并带一个指回原练习的 `📍来源` 反向引用。

**`js_notes/README.md` 含有具有约束力的辅导规则 —— 教学前先读它。**
其中最关键的几条:

- **不替用户写答案。** 在练习文件里,"轮到你"区块只给要求 —— 不给解答,
连注释掉的解答也不行(那会破坏练习)。提示只指方向("这两个方法可以链式
调用"),不轻易给出完整的一整行,比如 `raw.trim().toLowerCase()`。
- **让用户自己修自己的代码。** 指出哪里错了、为什么错;不要替他改练习答案。
- 演示区块(教学示例)可以用注释写出预期输出;练习区块不可以。
- **当用户犯了新错误时,把它吸收进 `00-concepts/my-mistakes.md`**
(错法→现象→正解→教训 格式,带 `📍来源` 索引);或者如果不值当单开一条,就直接在那个笔记里 Edit 也行,这样复习时可追溯。
但**只记真正有教益的错误** —— 已被现有条目覆盖的纯手误,或用户已经自己
修好、没有新东西可学的错误,不值得新开一条(记流水账的话用户会反驳)。
拿不准时,问一下。
- 一个主题完成后,更新 `js_notes/README.md` 里的进度勾选框。

### 一章收尾必做清单(用户的长期要求)⭐

当一章的练习全部做完、确认无误时,**每次都要走完这套收尾流程**(别遗漏、别偷懒):

1. **跑一遍验证**:`node js_notes/NN-topic.js` 确认整章(含用户练习答案)能跑、
输出符合预期;必要时 `node --check` 先查语法。
2. **留学习痕迹**:用户练习中犯过的错,按"改用户代码时留学习痕迹"那条,在文件里
保留 `❌/✅` 对照(绝不留能跑的坏代码)。
3. **吸收错误**:本章冒出的、有教益的新错误,按上面规则并入 `my-mistakes.md`
(值当就新开条目、不值当就在相关条目补一句;纯手误/已覆盖的不记)。
4. **更新进度**:勾选 `js_notes/README.md` 里该章的进度框(`[ ]` → `[x]`),
必要时补上本章实际覆盖到的关键词。
5. **提交**:按逻辑拆成干净的 commit 提交(Angular 风格 message);工作区留干净。
6. **导向下一步**:简述下一章要学什么、和已学内容/pi 目标的关联,让用户决定是否继续。

### 改用户代码时留下学习痕迹(用户的长期要求)

每当用户让你改他的代码或加标记时,**把错误版本作为注释掉的 `❌ / ✅` 对照
保留**在修复的旁边 —— 好让未来的他看到*错在哪、为什么错*,而不仅仅是改对后的
那一行。这与用户已经在 `06-arrays.js` / `07-objects.js` 里用的风格一致:

```js
// ❌ splice 返回的是"被删除的元素",不是修改后的数组
// console.log(q.splice(1, 0, 'second'))
// ✅
q.splice(1, 0, 'second')
console.log(q)
```

这类痕迹的规则:
- 把错误行注释掉(绝不留下能跑的坏代码),放在修复的**上方**。
- 一行简短的 `// ❌ 原因` 说明*为什么*错;`// ✅` 标出正确的代码。
- 这适用于**演示/教学区块,以及用户明确要求的修复** ——
它**不**推翻"绝不写答案":对于用户还没动手的 `轮到你` 练习,仍然不要
往里丢解答。
- 由此浮现出的、有分量的新陷阱,通常也应加一条 `my-mistakes.md` 条目
(以上文"只记有教益的"这条为准)。

### 讲清*为什么*,而不只是规则(用户的长期要求)

比起干巴巴的"就是这么规定的",用户从底层机制里学得快得多。只给规则、
不给原因的简略笔记是不够的 —— 用户明确指出 `// 带 {} 就得自己写 return`
太晦涩。当一条规则有其原因时,**先讲原因**;机制一旦讲清,规则就不言自明
(而且往往好几条规则会归结到同一个原因)。

举例说明 —— 箭头函数的函数体与 `return`。**先说出那句最核心的话**,再让
一切作为它的推论自然而然地推出(能找到那句核心才是本事 —— 用户曾修正过一版
从 `{` 的两种身份出发、而非从那句核心出发的草稿):
- **核心一句:`=>` 的右侧期待一个*表达式***(某个求值后得到一个值的东西)。
- 给它一个表达式 → 那*就是*那个值 → JS 把它返回(隐式返回)。
在那里写 `return` 是非法的:`return` 是*语句*,不是表达式,所以
`(a, b) => return a + b` 会 `SyntaxError`。
- `{ ... }` 是逃生舱:以 `{` 开头的函数体会让 JS 把它当作*语句块*,
语句块不产生值 → 你必须显式 `return`,否则得到 `undefined`。
- 由这同一句核心推出:对象字面量也以 `{` 开头,于是和语句块规则冲突 →
用括号包起来以强制进入表达式上下文:`(x) => ({ name: x })`。

由此得出的实操习惯:
- 优先说"X **因为**解析器/引擎看到了 Y",而不是"总是这样做 X"。
- **用运行代码来验证**,当它能让论点更锋利时 —— 用户看重看到真实的报错
信息或输出,胜过一句断言(例如用 `node --check` 显示一个真实的
`SyntaxError`)。

### 讲解【直接写进文件】,别只留在聊天里(用户的长期要求)⭐

用户反馈:讲解如果只写在聊天回复里,事后几乎不会再翻出来;而临时验证文件
(`/tmp/*.js`)他更是看不到。所以**成品是文件,不是聊天**。当一段讲解值得
留存时:
- **直接写进对应的 `NN-topic.js`**(概念区讲原理、示范区放可跑代码),
而不是"先在聊天里长篇讲一遍、再写一遍文件" —— 那样既浪费上下文,产物
用户又拿不到。聊天里只做**极简导读 + 指向文件哪一节**。
- 临时用 `/tmp/*.js` 跑验证没问题,但**结论要落进文件**;别把只存在于
`/tmp` 或聊天里的解释当成交付。
- 跨主题的点,写进该去的那一章,并在相关章节挂一句呼应(例:提升 hoisting
写进 10-functions.js,在 02-variables.js 的 var 那格挂一句指过去)。
- 这条和上面"讲清为什么""留 ❌/✅ 痕迹"是一套:原理讲清 + 落进文件 +
可追溯,才是一次完整的讲解。

## 运行代码

这里**没有构建系统、没有 package.json、没有 linter、没有测试套件** ——
这是一个学习仓库,不是一个应用。代码逐个文件运行:

```bash
node js_notes/06-arrays.js # 运行单个主题文件
node --check js_notes/09-loops.js # 只做语法检查,不执行
```

在编辑器里,用户也会用 **Quokka.js**(内联显示实时 `console.log` 值;免费版 ——
不支持 `//?` 实时注释)或 **Code Runner**(▷ 按钮)来运行文件。

**自足文件 vs 浏览器文件:** 一个 `.js` 文件如果自己定义了它用到的一切、
且不碰 `document`,就能在 Node/Quokka/Code Runner 下运行。而依赖另一个脚本里
变量的文件,或使用 `document`/DOM 的文件(第 21 天以后以及那些小项目),
必须通过它们的 `index.html` 在浏览器里打开、配合 F12 控制台 —— 在 Node 下
运行会抛错(例如 `firstName is not defined`、`document is not defined`)。

## 内容语言

散文、笔记、讲解用**中文**;代码、标识符、commit message 用英文。
Commit message 遵循 Angular 风格(`<type>(scope): description`)。

## Git 远端

这个克隆是从原课程 fork 出来的:

- `origin` → `github.com/JaydonZhao/30-Days-Of-JavaScript`(用户的 fork;**推到这里**)
- `upstream` → `github.com/Asabeneh/30-Days-Of-JavaScript`(原作者;拉取教程更新用
`git fetch upstream && git merge upstream/master` —— 用 merge,不用 rebase)

工作直接发生在 `master` 上(这个 fork 的 `master` 独立于 upstream 的,所以
用户的提交永远不会碰到别人的仓库)。这个 fork 是**公开的** —— 不要提交任何
私密内容。
67 changes: 67 additions & 0 deletions js_notes/00-concepts/call-forms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 通用概念 · 三种调用形式:`值.方法()` vs `函数()` vs `类.方法()`

> 跨主题概念,不属于某一天。困惑"什么时候用点、什么时候传参"时看这篇。

## 核心问题:这个函数"属于"谁?

### 形式一:`值.方法()` —— 方法(method)
函数**属于某个值/对象**,是它"自带的能力",用点 `.` 调用。
**点前面那个值 = 被操作的主角**,不用再传进去。

```js
'hello'.toUpperCase() // toUpperCase 是字符串自带的能力
;[1, 2, 3].map((x) => x * 2) // map 是数组自带的能力
'a,b'.split(',') // split 属于字符串
```

### 形式二:`函数(值)` —— 独立函数(function)
函数**不属于任何特定值**,是自由的工具,数据当参数**传进括号**。

```js
Number('42') // 转谁就传谁进去
parseInt('42px')
isNaN(x)
```

### 形式三:`类.方法(值)` —— 静态方法(static method)
函数挂在某个**类/工具箱**下做归类,数据仍然**传进去**。

```js
Array.isArray([1, 2, 3]) // isArray 挂在 Array 名下
Math.max(1, 2, 3) // max 挂在 Math 名下
Number.isNaN(x) // 挂在 Number 名下
Object.keys(obj) // 挂在 Object 名下
```

> ⚠️ 区分:`Array.isArray(arr)` 的 `Array`(大写=类/工具箱)和 `[1,2,3].map()` 点前的
> 数组实例(具体的值)是两回事。**大写开头 = 工具箱;点前是具体值 = 实例。**

## 对照表

| 形式 | 例子 | 数据在哪 | 感觉 |
|---|---|---|---|
| `值.方法()` | `'hi'.toUpperCase()` | **点前面** | 值自带的能力 |
| `函数(值)` | `Number('42')` | **括号里** | 自由工具 |
| `类.方法(值)` | `Array.isArray(x)` | **括号里** | 工具箱里的工具 |

## "什么时候用哪个"?

**真相:不是你选的,是语言设计时定死的。** 每个功能造出来就固定了形式,只能记/查。
- 转大写只能 `str.toUpperCase()`,不能 `toUpperCase(str)`
- 判断数组只能 `Array.isArray(x)`,不能 `x.isArray()`

但有规律可猜:
1. **操作字符串/数组/对象内容的** → 大多 `值.方法()`
2. **类型转换/数学/解析类工具** → 大多 `函数()` 或 `类.方法()`(`Number`、`Math.`、`JSON.parse`)
3. **`Math.` / `JSON.` / `Object.` / `Array.` 开头** → 几乎都是静态方法,数据传进去

## 记忆诀窍

> 问:**"这是某个值天生就会的,还是外部工具对它做的?"**
> - 天生就会(大写化、截取、拆分)→ `值.方法()`
> - 外部工具处理它(转类型、算最值、解析)→ `函数(值)` / `工具箱.方法(值)`

## 🔧 实用技巧

**不确定时,在编辑器里敲 `值.` 看自动补全弹什么** —— 弹出来的就是它自带的方法;
没弹出来的,就得找独立函数或静态方法。让 VS Code 帮你查最快。
Loading