Files
test-repo/docs/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive.md
T
sanbuphy 47377646df feat(docs): enhance JavaScript runtime and browser-as-os content
refactor(demos): improve variable box, scope, and type annotation demos
style(demos): update visual styles and animations for better UX
docs(browser-as-os): restructure content with tables and practical examples
feat(demos): add new TypeScript and runtime environment demos
2026-02-17 01:39:59 +08:00

858 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# JavaScript 深度指南
::: tip 前言
你已经学会了 HTML 和 CSS,能做出好看的网页了。但你可能会发现:点击按钮没反应,填了表单提交不了,网页就像一张"静态"的图片。
这就是我们需要 JavaScript 的原因——它让网页"活"起来。点击按钮能弹出菜单,输入文字能实时搜索,滚动页面能加载更多内容……这些交互效果都靠 JavaScript。
在 vibecoding 里,AI 会帮你写大部分代码。但你至少得能看懂代码在做什么,否则 AI 写错了你也发现不了。读完这篇,你就能:
- 读懂 AI 写的代码在做什么
- 看出代码哪里有问题
- 用清晰的话告诉 AI 怎么改
:::
**这篇文章会带你学什么?**
| 章节 | 内容 | 学完能干嘛 |
|-----|------|-----------|
| **第 1 章** | JavaScript 是什么 | 明白它在网页里扮演什么角色 |
| **第 2 章** | 数据与变量 | 知道程序怎么存东西、怎么用东西 |
| **第 3 章** | 函数与逻辑 | 看懂代码的判断、循环和复用逻辑 |
| **第 4 章** | DOM 与事件 | 知道代码怎么控制网页、怎么响应用户操作 |
| **第 5 章** | 实战技巧 | 拿到 AI 代码怎么读、遇到报错怎么说 |
每一章都从"能识别代码"开始,不需要你会手写。遇到不懂的代码,随时回来查就行。
---
## 1. JavaScript 是什么
::: tip 🤔 核心问题
**为什么网页需要 JavaScript** HTML 和 CSS 已经能让网页有内容、有样式了,为什么还要学一门新语言?
:::
### 1.1 从"静态网页"到"动态应用"
<div style="display: flex; gap: 20px; margin: 20px 0;">
<div style="flex: 1; padding: 16px; border: 1px solid #e4e7ed; border-radius: 12px;">
**📄 没有 JavaScript 的网页**
- 内容固定,无法交互
- 点击按钮没反应
- 填写表单提交不了
- 页面不会自动更新
*就像一张纸质海报,只能看*
</div>
<div style="flex: 1; padding: 16px; border: 1px solid #e4e7ed; border-radius: 12px;">
**🚀 有 JavaScript 的网页**
- 点击按钮弹出菜单
- 输入文字实时搜索
- 滚动自动加载内容
- 数据实时更新显示
*就像一个真正的应用程序*
</div>
</div>
**用一句话理解三者的关系:**
| 技术 | 比喻 | 作用 |
|------|------|------|
| **HTML** | 骨架 | 定义网页的结构和内容 |
| **CSS** | 皮肤 | 定义网页的外观和样式 |
| **JavaScript** | 肌肉和神经系统 | 让网页能响应、能交互、能思考 |
### 1.2 为什么 vibecoding 也需要懂 JavaScript
::: warning 刚学 JS 的开发者踩坑记
一位刚学 JavaScript 的开发者用 AI 做了一个"计数器"应用:点击按钮,数字加 1。AI 生成的代码能正常工作。
但他想改成"点击加 2",对 AI 说:"让每次点击加 2。" AI 改了代码,可数字还是只加 1。
他问 AI 为啥没效果,AI 解释了一通,但他看不懂代码里的 `count = count + 1` 是什么意思,也不知道 AI 改的是不是这个地方。只能反复说"加 2 没效果",AI 又改了好几版,有的把初始值改成 2,有的在完全不相关的地方加了 2。
最后他看了第 2 章"变量"的概念,明白了 `count = count + 1` 是在把 count 的值加 1 再存回去。然后他对 AI 说:"把 `count + 1` 改成 `count + 2`。"
一次就改对了。
**这就是为什么要懂 JavaScript——不是为了手写代码,而是为了在 AI 没改对时,你能一眼看出问题在哪,一句话说到点子上。**
:::
### 1.3 先睹为快:一段真实的 AI 代码
在深入学习之前,让我们先看一段 AI 生成的真实代码。不要担心看不懂,只要有个印象,后面我们会逐一讲解每个部分。
**场景**:做一个"点击按钮切换背景颜色"的功能
```javascript
// 定义一组颜色
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4']
let currentIndex = 0
// 找到页面上的按钮
const button = document.querySelector('#changeBtn')
// 给按钮添加点击事件
button.addEventListener('click', () => {
currentIndex = (currentIndex + 1) % colors.length
document.body.style.backgroundColor = colors[currentIndex]
})
```
**这段代码在做什么?**
| 代码 | 作用 | 对应章节 |
|------|------|----------|
| `const colors = [...]` | 定义一组颜色数据 | 第 2 章:数组 |
| `let currentIndex = 0` | 记录当前显示第几个颜色 | 第 2 章:变量 |
| `document.querySelector(...)` | 找到页面上的按钮 | 第 4 章:DOM 查找 |
| `button.addEventListener(...)` | 给按钮添加点击事件 | 第 4 章:事件监听 |
| `() => {...}` | 定义点击后要执行的代码 | 第 3 章:箭头函数 |
::: info 💡 核心启示
你不需要现在就理解每一行代码。只要记住:**JavaScript 代码就是一系列指令,告诉浏览器"当用户做某事时,应该发生什么"。**
:::
---
## 2. 数据篇:变量与数据类型
::: tip 🤔 核心问题
**程序是怎么"记住"东西的?** 用户输入的内容、从服务器获取的数据、计算过程中的中间结果——这些信息都存在哪里?
:::
### 2.1 变量:给数据起个名字
**变量就像一个有标签的盒子**——你可以把数据放进去,以后通过标签来取用。
```javascript
const name = "张三" // 名字不会变,用 const
let age = 25 // 年龄可能会变,用 let
```
**为什么要区分 const 和 let**
想象一下:你的身份证号码(const)这辈子都不会变,但你的年龄(let)每年都会变。JavaScript 让你用不同的关键字来表达这种"变与不变"的意图。
| 关键字 | 能否修改 | 使用场景 | 示例 |
|--------|---------|----------|------|
| `const` | ❌ 不能 | 值不会变的数据 | 身份证号、配置项、颜色列表 |
| `let` | ✅ 能 | 值会变化的数据 | 计数器、当前选中的选项、用户输入 |
::: details 🔍 看一个具体的例子
```javascript
// 用 const:这些值不会变
const PI = 3.14159
const MAX_USERS = 100
const APP_NAME = "TodoList"
// 用 let:这些值会变化
let count = 0
count = 1 // ✅ 可以修改
count = count + 1 // ✅ 可以基于原值计算
// 如果用 const 会怎样?
const fixedCount = 0
fixedCount = 1 // ❌ 报错!const 不能重新赋值
```
:::
👇 **动手试试看**:修改下面的代码,看看 const 和 let 的区别
<VariableBoxDemo />
### 2.2 数据类型:JavaScript 里的几种"东西"
JavaScript 把数据分成几种类型,最常用的有三种:
| 类型 | 说明 | 示例 | 实际场景 |
|------|------|------|----------|
| `string`(字符串)| 文本内容 | `"hello"`, `'你好'` | 用户名、商品描述、提示信息 |
| `number`(数字)| 数值 | `42`, `3.14` | 价格、数量、评分 |
| `boolean`(布尔值)| 是/否 | `true`, `false` | 是否登录、是否完成、是否可见 |
**还有两个特殊值需要知道:**
- `undefined` → 变量声明了,但还没给值
- `null` → 故意设为空(表示"这里没有值")
::: details 🔍 模板字符串:更方便地拼接文本
在 AI 代码里,你经常会看到用反引号(`` ` ``)包裹的字符串,里面还有 `${...}`
```javascript
const name = "张三"
const age = 25
// 传统写法(麻烦)
const message = "我叫" + name + ",今年" + age + "岁"
// 模板字符串(简洁)
const message = `我叫${name},今年${age}岁`
// 结果:"我叫张三,今年25岁"
```
**识别要点**:看到反引号和 `${}`,就知道是在把变量插入到文本中。
:::
### 2.3 对象和数组:把数据组织起来
**对象 = 一组有名字的属性**(像一张个人信息表)
```javascript
const user = {
name: "张三",
age: 25,
isVIP: true
}
// 使用点号访问属性
console.log(user.name) // "张三"
console.log(user.age) // 25
```
**数组 = 一组有顺序的数据**(像一个列表)
```javascript
const colors = ['红色', '绿色', '蓝色']
// 用索引访问(从 0 开始)
console.log(colors[0]) // "红色"
console.log(colors[1]) // "绿色"
```
**嵌套结构:对象里套数组、数组里套对象**
这是 AI 代码中最常见的数据结构:
```javascript
const todos = [
{ id: 1, text: "学习 JavaScript", done: false },
{ id: 2, text: "做项目", done: true },
{ id: 3, text: "写文档", done: false }
]
// 访问:先取数组的第 0 项,再取它的 text 属性
console.log(todos[0].text) // "学习 JavaScript"
```
::: info 💡 识别技巧
- 看到 `{}` → 这是一个对象,里面是一组 `名字: 值`
- 看到 `[]` → 这是一个数组,里面是一组按顺序排列的值
- 看到 `data[0].name` → 先取数组第 0 项,再取它的 name 属性
:::
### 2.4 值与引用:一个容易踩的坑
这是新手最常遇到的问题之一!
**基本类型(string、number、boolean)赋值 = 复制一份全新的数据:**
```javascript
let a = 10
let b = a // b 得到 a 的副本
b = 20
console.log(a) // 10a 不受影响)
```
**对象和数组赋值 = 复制的是"地址"(指向同一个东西):**
```javascript
let user1 = { name: "张三" }
let user2 = user1 // user2 指向同一个对象
user2.name = "李四" // 修改 user2 会影响 user1
console.log(user1.name) // "李四"user1 也变了!)
```
**为什么要创建副本?**
在 React/Vue 中,直接修改数据会导致界面不更新。所以 AI 代码里经常看到 `[...array]` 或 `{...obj}`——它在创建副本,避免互相影响。
```javascript
// 用展开运算符创建副本
const arr1 = [1, 2, 3]
const arr2 = [...arr1] // 创建新数组
arr2.push(4)
console.log(arr1) // [1, 2, 3](不受影响)
console.log(arr2) // [1, 2, 3, 4]
```
👇 **动手试试看**:观察修改副本时原数据的变化
<ReferenceDemo />
### 2.5 解构与展开:现代 JavaScript 的快捷写法
这两个语法在 AI 代码里到处都是,不认识就读不懂代码。
**解构赋值:从对象或数组里快速提取数据**
```javascript
const user = { name: "张三", age: 25, city: "北京" }
// 传统写法(麻烦)
const name = user.name
const age = user.age
// 解构写法(简洁)
const { name, age } = user
// 效果一样,但一行搞定
```
**展开运算符:复制并扩展数据**
```javascript
// 复制数组并添加新元素
const arr1 = [1, 2, 3]
const arr2 = [...arr1, 4, 5] // [1, 2, 3, 4, 5]
// 复制对象并添加新属性
const user1 = { name: "张三", age: 25 }
const user2 = { ...user1, city: "北京" }
// { name: "张三", age: 25, city: "北京" }
```
::: info 💡 识别技巧
- 看到 `const { name, age } = person` → 从 person 对象里提取 name 和 age
- 看到 `...array` 或 `...obj` → 把数组或对象展开铺平
- 你不需要能手写,但必须能读懂
:::
---
## 3. 逻辑篇:函数与流程控制
::: tip 🤔 核心问题
**代码是怎么"做决定"和"重复做事"的?** 程序需要根据条件执行不同的操作,也需要重复执行某些任务——这些逻辑怎么表达?
:::
### 3.1 条件判断:如果...就...否则...
**if/else:最基本的条件判断**
```javascript
const age = 18
if (age >= 18) {
console.log("成年人")
} else {
console.log("未成年")
}
```
**三元运算符:简写的 if/else**
```javascript
// 完整写法(4 行)
let message
if (age >= 18) {
message = "成年人"
} else {
message = "未成年"
}
// 三元运算符(1 行)
const message = age >= 18 ? "成年人" : "未成年"
// 格式:条件 ? 条件为真时的值 : 条件为假时的值
```
**&& 短路写法:React 代码里常见**
```javascript
// 只有 isLoggedIn 为 true 时才显示用户面板
isLoggedIn && <UserPanel />
// 等价于
if (isLoggedIn) {
return <UserPanel />
}
```
::: info 💡 识别技巧
- 看到 `? :` → 这是三元运算符,简写的 if/else
- 看到 `&&` → 前面为 true 才执行后面
:::
### 3.2 函数:把操作打包起来
**函数 = 一道菜的配方**
- 定义函数 = 写下配方
- 调用函数 = 按配方做菜
- 参数 = 原料
- 返回值 = 成品
```javascript
// 定义函数(写下配方)
function greet(name) {
return "Hello " + name
}
// 调用函数(按配方做菜)
console.log(greet("张三")) // "Hello 张三"
console.log(greet("李四")) // "Hello 李四"
```
**三种写法,一眼识别:**
```javascript
// 1. function 声明(传统写法)
function greet(name) {
return "Hello " + name
}
// 2. 箭头函数(AI 代码里用得最多)
const greet = (name) => {
return "Hello " + name
}
// 3. 箭头函数简写(只有一行时)
const greet = (name) => "Hello " + name
```
👇 **动手试试看**:输入不同的名字,看看函数怎么工作
<FunctionMachineDemo />
::: info 💡 识别技巧
- 看到 `function` 或 `=>` → 这是一个函数
- 看到 `fn()` → 在调用这个函数
- 看到 `() => {}` → 箭头函数,现代 JS 的主流写法
:::
### 3.3 数组方法:处理列表的利器
在 React/Vue 里,几乎每个列表渲染都会用到这些方法。
```javascript
const todos = [
{ id: 1, text: "学习", done: false },
{ id: 2, text: "工作", done: true }
]
// .map():把数组的每一项变成另一个东西
const texts = todos.map(todo => todo.text)
// ["学习", "工作"]
// .filter():筛选出符合条件的项
const unfinished = todos.filter(todo => !todo.done)
// [{ id: 1, text: "学习", done: false }]
// .find():找到第一个符合条件的项
const found = todos.find(todo => todo.id === 1)
// { id: 1, text: "学习", done: false }
```
::: info 💡 识别技巧
- 看到 `.map()` → 对数组做变换,返回新数组
- 看到 `.filter()` → 筛选数组
- 看到 `items.map(item => <li>{item.name}</li>)` → 把每个数据项变成列表标签
:::
### 3.4 作用域:变量的"可见范围"
**用"房间"比喻:**
- 函数内部的变量就像房间里的东西,外面看不到
- 但房间里的人可以看到走廊(外层作用域)的东西
```javascript
const global = "全局变量" // 走廊里的东西
function room() {
const local = "房间里的东西" // 房间里的东西
console.log(global) // ✅ 能看到走廊
}
console.log(local) // ❌ 报错!外面看不到房间里的东西
```
**核心直觉:** 代码写在哪里,决定了它能看到什么变量。
👇 **动手试试看**:点击不同的作用域,看看能访问哪些变量
<ScopeDemo />
### 3.5 闭包:函数"记住"了它诞生时的环境
**不要把它当成独立的概念,从一个具体场景理解:**
```javascript
function setupCounter() {
let count = 0 // 这个变量在函数内部
return {
add: () => { count++; return count },
getCount: () => count
}
}
const counter = setupCounter()
console.log(counter.add()) // 1
console.log(counter.add()) // 2
console.log(counter.getCount()) // 2
```
**核心直觉:** 函数在被创建时,会"记住"它周围的变量,即使外层函数已经执行完了。
👇 **动手试试看**:观察闭包如何让函数"记住"状态
<ClosureDemo />
### 3.6 this:函数被谁调用
**不讲复杂的绑定规则,只讲最常见的场景:**
**场景 1:在对象的方法里,this 指向这个对象**
```javascript
const user = {
name: "张三",
sayHi() {
console.log("你好,我是" + this.name) // this 指向 user
}
}
user.sayHi() // "你好,我是张三"
```
**场景 2:在事件监听里,this 指向触发事件的元素**
```javascript
button.addEventListener('click', function() {
console.log(this) // this 指向 button 元素
})
// 但箭头函数不会改变 this
button.addEventListener('click', () => {
console.log(this) // this 指向外层的 this
})
```
::: info 💡 遇到问题怎么办?
如果 AI 代码里出现 this 相关的 bug(比如 `Cannot read property of undefined`),告诉 AI:"这个方法里的 this 指向不对,改成箭头函数或者用 bind"
:::
---
## 4. 交互篇:DOM、事件与异步
::: tip 🤔 核心问题
**JavaScript 怎么跟网页"互动"?** 怎么找到页面上的元素?怎么响应用户的点击、输入?怎么从服务器获取数据?
:::
### 4.1 DOMJavaScript 看到的网页
网页在 JavaScript 眼里是一棵"树",每个 HTML 标签都是树上的一个"节点"。
```html
<html>
<body>
<h1>标题</h1>
<p>段落</p>
<ul>
<li>项目1</li>
<li>项目2</li>
</ul>
</body>
</html>
```
**JS 操控网页 = 找到节点 + 修改节点 + 创建/删除节点**
👇 **动手试试看**:点击节点,看看 DOM 树是怎么组织的
<DOMTreeDemo />
### 4.2 查找与修改元素
**查找元素:**
```javascript
// 根据 CSS 选择器查找(最常用)
const title = document.querySelector('h1') // 找第一个 h1
const button = document.querySelector('#btn') // 找 id="btn" 的元素
const items = document.querySelectorAll('.item') // 找所有 class="item" 的元素
```
**修改元素:**
```javascript
// 改文字
title.textContent = "新标题"
// 改样式
element.style.color = "red"
element.style.fontSize = "20px"
// 改 CSS 类
element.classList.add('active') // 添加类
element.classList.remove('hidden') // 移除类
element.classList.toggle('open') // 切换类(有就移除,没有就添加)
```
::: info 💡 识别技巧
- 看到 `document.querySelector` → 在查找网页元素
- 看到 `.textContent` → 改文字
- 看到 `.style.xxx` → 改样式
- 看到 `.classList.add/remove/toggle` → 改 CSS 类
:::
### 4.3 事件:当用户做了某个操作时...
**addEventListener:给元素添加事件监听**
```javascript
button.addEventListener('click', () => {
console.log("按钮被点击了")
})
```
**常见事件:**
| 事件 | 触发时机 | 实际场景 |
|------|---------|----------|
| `click` | 点击 | 按钮点击、链接跳转 |
| `input` | 输入框内容变化 | 实时搜索、表单验证 |
| `submit` | 表单提交 | 登录、注册、提交数据 |
| `scroll` | 滚动页面 | 懒加载、回到顶部 |
**事件对象:获取更多信息**
```javascript
input.addEventListener('input', (e) => {
console.log(e.target.value) // 获取输入框的值
e.preventDefault() // 阻止默认行为(比如表单提交后刷新页面)
})
```
::: info 💡 实际应用
当你想给按钮加一个功能,本质上就是在告诉 AI:"给这个按钮添加一个点击事件,点击后执行某某操作"
:::
### 4.4 异步:为什么有些操作不是立刻完成的
**餐厅比喻:**
点菜后不用站在厨房门口等,可以先做别的事,菜好了服务员会端过来。
**最常见场景:从服务器获取数据**
```javascript
// 同步写法(会卡住页面,不要用)
const data = fetch('/api/data') // ❌ 这样写会卡住
// 异步写法(正确)
async function loadData() {
try {
const response = await fetch('/api/data')
const data = await response.json()
console.log(data)
} catch (error) {
console.error('出错了:', error)
}
}
```
**async/await 语法:**
- `async` → 标记这个函数里有异步操作
- `await` → 等待这个操作完成(但不会卡住页面)
- `try/catch` → 处理可能出现的错误
👇 **动手试试看**:观察异步操作的执行顺序
<AsyncRestaurantDemo />
::: info 💡 识别技巧
- 看到 `async/await` → 在等待耗时操作
- 看到 `fetch()` → 在从服务器获取数据
- 看到 `try/catch` → 在处理可能的错误
:::
### 4.5 事件循环:JavaScript 到底怎么工作的
**不用术语"微任务/宏任务",用一个简单的模型理解:**
**JS 是一个"单人工位"**,同时只做一件事,但有一个"待办便签栏"(任务队列)。
当遇到要等待的操作(网络请求、定时器),JS 不是傻等,而是把"等好了之后做什么"贴到便签栏,自己继续往下执行。等当前事情做完了,才去看便签栏。
```javascript
console.log("1")
setTimeout(() => console.log("2"), 0) // 即使是 0 秒,也会推迟
console.log("3")
// 输出:1, 3, 2(不是 1, 2, 3!)
```
**为什么?**
1. 执行 `console.log("1")` → 输出 1
2. 遇到 `setTimeout` → 把回调贴到便签栏,继续往下
3. 执行 `console.log("3")` → 输出 3
4. 当前代码执行完了,去看便签栏
5. 执行 `setTimeout` 的回调 → 输出 2
👇 **动手试试看**:观察代码的执行顺序
<JSEventLoopDemo />
::: info 💡 遇到问题怎么办?
如果 AI 代码里数据还没获取到页面就渲染了,告诉 AI:"数据还没加载完就开始渲染了,需要添加 loading 状态,等数据到了再渲染"
:::
### 4.6 模块:import 和 export
AI 生成的 React/Vue 代码第一行几乎都是 `import`。
**import = 从别的文件引入功能**
```javascript
// 从工具文件引入函数
import { formatDate } from './utils'
// 从第三方包引入
import React from 'react'
import { useState } from 'react'
```
**export = 把功能暴露出去给别人用**
```javascript
// utils.js
export function formatDate(date) {
// ...
}
// 或者默认导出
export default function formatDate(date) {
// ...
}
```
**npm 包 = 别人写好的工具,安装后就能用**
```javascript
// 安装包:npm install lodash
// 使用包
import _ from 'lodash'
```
::: info 💡 识别技巧
- 看到 `import` → 从别的文件引入功能
- 看到 `export` → 把功能暴露给别人用
- 看到 `from 'react'` → 从 React 包引入
- 看到 `from './utils'` → 从本地文件引入
:::
---
## 5. 实战篇:读懂代码、看懂报错、精准描述
::: tip 🤔 核心问题
**前面学了这么多语法,实际拿到 AI 代码时怎么用?** 怎么快速读懂代码?遇到报错怎么办?怎么让 AI 准确地帮你改代码?
:::
### 5.1 拿到 AI 代码后怎么读
**四步法:**
| 步骤 | 看什么 | 示例 |
|------|--------|------|
| **第一步:看整体结构** | 有几个函数?分别做什么? | `loadData()` 加载数据,`renderList()` 渲染列表 |
| **第二步:找入口** | 程序从哪里开始执行? | `addEventListener('click', ...)` 点击时开始 |
| **第三步:追踪数据流** | 数据从哪里来?到哪里去? | 从 API 获取 → 解析 → 渲染到页面 |
| **第四步:看细节逻辑** | 具体函数里怎么处理的? | 循环、判断、计算 |
**用第 1 章的代码示例做一次完整的"阅读演示"**
```javascript
// 第一步:整体结构
// - 一个颜色数组
// - 一个变量记录当前索引
// - 一个按钮的点击事件
// 第二步:入口点
// button.addEventListener('click', ...) → 点击按钮时执行
// 第三步:数据流
// colors(颜色数组)→ currentIndex(当前索引)→ backgroundColor(背景色)
// 第四步:细节逻辑
// currentIndex = (currentIndex + 1) % colors.length
// 这个公式的意思:每次 +1,但不超过数组长度(循环)
```
### 5.2 常见报错速查
| 报错 | 大白话解释 | 怎么跟 AI 说 |
|------|-----------|-------------|
| `TypeError: Cannot read properties of undefined` | 你想从一个不存在的东西上取值 | "第 X 行报错,某某变量是 undefined,检查它的赋值逻辑" |
| `ReferenceError: xxx is not defined` | 用了一个没有声明过的变量名 | "变量 xxx 没有定义,是不是拼写错了或者忘了导入" |
| `TypeError: xxx is not a function` | 把一个不是函数的东西当函数调用了 | "xxx 不是函数,检查一下它的类型和来源" |
| `SyntaxError: Unexpected token` | 语法写错了(括号不匹配、少了逗号等) | "第 X 行语法错误,检查括号和标点" |
| `CORS error` | 浏览器阻止了跨域请求 | "遇到 CORS 错误,需要配置跨域资源共享" |
| `404 Not Found` | 请求的资源不存在 | "API 返回 404,检查接口地址是否正确" |
### 5.3 如何精准描述问题
新手和熟练开发者的差距,往往就体现在**描述问题的精准度**上。
| ❌ 差的描述 | ✅ 好的描述 |
|-----------|-----------|
| "代码有 bug" | "点击删除按钮时,删除的不是当前项而是最后一项" |
| "样式不对" | "标题应该居中,现在是左对齐" |
| "数据显示不出来" | "fetch 请求返回了数据(控制台能看到),但页面没有重新渲染" |
| "加一个功能" | "在用户列表页面添加一个搜索框,输入时实时过滤列表,按 name 字段模糊匹配" |
| "点击没反应" | "点击按钮时控制台报错 'Cannot read property of undefined',错误在第 X 行" |
**一个实战练习:**
```javascript
// 有 bug 的代码
function deleteTodo(index) {
todos.splice(index, 1) // 总是删除最后一项
}
// 错误现象:无论点哪个删除按钮,删的都是最后一项
```
**❌ 差的描述:** "删除功能有 bug"
**✅ 好的描述:** "点击删除按钮时,删除的不是当前项而是最后一项。代码里用了 splice(index, 1),但 index 可能不正确。需要改成用每个事项的唯一 id 来匹配删除。"
### 5.4 你现在应该能识别的代码
- 看到 `const/let` → 知道变量能不能重新赋值
- 看到 `{}` → 对象 / 看到 `[]` → 数组
- 看到 `{...obj}` 或 `[...arr]` → 在创建副本
- 看到 `function` 或 `=>` → 定义了一段可重复执行的操作
- 看到 `if/else` 或 `? :` → 代码在做判断
- 看到 `.map()` / `.filter()` → 在变换或筛选数组
- 看到 `document.querySelector` → 在查找网页元素
- 看到 `addEventListener` → 在监听用户操作
- 看到 `async/await` → 在等待耗时操作
- 看到 `import/export` → 在引入或导出模块
- 遇到报错 → 能读懂大意并精准描述给 AI
**如果你认真读了每章的"深入"部分,你还掌握了这些核心概念:**
- **值 vs 引用**:基本类型复制值,对象/数组复制的是地址
- **作用域与闭包**:函数能"记住"它诞生时周围的变量
- **this 的本质**:取决于函数被谁调用,而不是写在哪里
- **事件循环**:JS 是单线程的,靠任务队列实现"不阻塞"
这些概念会帮你更快定位问题。
::: info 💡 遇到问题时这样跟 AI 说
- "第 X 行报错 XXX,帮我看看是什么问题"
- "这个函数的逻辑是 XXX,但结果不对,应该是 XXX"
- "我想修改 XXX 功能,具体要求是 XXX"
:::