一、调试是什么

调试:

通过观察程序运行过程,找到程序状态偏离预期的位置。

程序运行:

1
2
3
4
5
输入

处理逻辑

输出

Bug:

1
2
3
4
5
6
7
8
9
输入正确



中间某一步状态错误



输出错误

调试核心:

不是:

1
猜哪里错

而是:

1
观察 → 定位 → 验证 → 修复

二、调试整体方向

一个完整系统:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
用户

浏览器

网络

网关/Nginx

服务器

业务代码

数据库

缓存/第三方服务

返回

浏览器显示

调试围绕:

1
2
3
4
5
6
1. 功能问题
2. 性能问题
3. 稳定性问题
4. 并发问题
5. 内存问题
6. 安全问题

三、功能正确性调试

目标:

为什么结果不符合预期?

例如:

用户登录失败:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
输入账号密码



前端



HTTP请求



后端验证



数据库查询



返回结果

需要定位:

1
哪一步数据错误

四、Debug核心概念

1. Breakpoint(断点)

作用:

1
让程序暂停在指定位置

例如:

1
2
3
4
5
6
7
function getUser(){

const user=findUser()

return user

}

点击:

1
const user=findUser()

运行:

1
2
3
4
5
6
7
8
9
程序执行



到这一行



暂停

暂停后可以观察:

1
2
3
变量
调用路径
执行流程

五、Call Stack(调用栈)

作用:

当前函数是怎么来的?

表示:

1
谁调用了谁

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function A(){

B()

}


function B(){

C()

}


function C(){

}

执行 C 时:

调用栈:

1
2
3
4
5
C

B

A

含义:

1
2
3
4
5
6
7
8
9
10
当前执行 C

C是谁调用?

B


B是谁调用?

A

栈规则

函数进入:

1
push 入栈

函数结束:

1
pop 出栈

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
main



a()



b()



c()

执行 c:

1
2
3
4
5
6
7
c

b

a

main

c结束:

1
2
3
4
5
b

a

main

六、同步调用栈

同步:

函数调用关系连续存在。

例如:

1
2
3
4
5
A(){

B()

}

执行:

1
2
3
4
5
A



B

调用栈:

1
2
3
B

A

B结束:

1
A

七、异步调用栈

异步:

不会一直占用原来的调用栈。

例如:

1
2
3
4
5
6
7
8
9
function handle(){

fetch()

.then(()=>{

})

}

执行:

第一阶段:

1
2
3
4
5
6
7
8
9
handle



注册Promise



handle结束

调用栈:

1

之后:

Promise完成:

新的任务:

1
Promise callback

重新入栈:

1
callback

看不到:

1
2
3
callback

handle

因为:

handle已经结束。


八、Async Stack(异步调用栈)

问题:

异步之后:

普通调用栈丢失上下文。

例如:

普通:

1
Promise callback

不知道:

是谁创建的。

Async Stack:

保存:

1
2
3
4
5
6
7
8
9
10
11
12
13
callback



fetchUser



handleRequest



click事件

作用:

找到:

1
异步任务来源

九、Scope(作用域)

作用:

当前暂停位置有哪些变量。


Local

当前函数变量。

例如:

1
2
3
4
5
function login(){

let user="Tom"

}

Scope:

1
2
3
Local

user="Tom"

Global

全局变量。

例如:

1
window.token="abc"

Script

脚本文件级变量。

例如:

1
const API="/user"

Closure(闭包)

函数保存外层变量。

例如:

1
2
3
4
5
6
7
8
9
10
function outer(){

let count=0


return function(){

}

}

内部函数仍然访问:

1
count

十、Chrome DevTools

1. Console

作用:

查看JS状态。

功能:

查看变量

1
user

执行表达式

1
user.name

修改变量

1
user.name="Jack"

新版:

Console = Evaluate Expression


2. Network

作用:

查看:

1
浏览器 ↔ 服务器

重点:


Headers

请求信息:

1
2
3
4
5
URL

Method

Status

Payload

发送数据:

例如:

1
2
3
{
username:"Tom"
}

Response

服务器返回。


Timing

分析请求耗时。

结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DNS



TCP



TLS



Request



Waiting(TTFB)



Download

Waiting(TTFB)

表示:

服务器多久返回第一个字节。

慢:

可能:

1
2
3
4
5
6
7
业务逻辑

数据库

Redis

第三方API

Download

表示:

数据下载。

慢:

可能:

1
2
3
4
5
数据太大

网络慢

没有压缩

十一、Breakpoint类型

1. 普通断点

指定代码暂停。


2. 条件断点

满足条件暂停。

例如:

1
id===100

用途:

大量请求中:

抓一个特殊请求。


3. Logpoint

打印变量。

不用修改代码:

1
console.log()

4. Exception Breakpoint

异常暂停。

作用:

1
2
3
哪里报错

哪里停

例如:

1
user.name

user为空:

自动暂停。


5. XHR / Fetch Breakpoint

作用:

监听网络请求。

例如:

设置:

1
/api/user

谁调用:

1
fetch("/api/user")

自动暂停。


6. Event Listener Breakpoint

监听事件。

例如:

1
2
click
keydown

点击按钮:

自动停到执行代码。


7. DOM Breakpoint

监听DOM变化。

例如:

不知道:

谁修改:

1
<div>

修改时暂停。


十二、Step调试

Step Over

执行下一行。

不进入函数。

适合:

1
函数内部不用看

Step Into

进入函数。

适合:

1
怀疑函数内部

Step Out

退出当前函数。

适合:

1
进入错误地方

十三、调试流程

遇到Bug:

第一步:复现

确认:

1
2
3
4
5
输入

步骤

结果

第二步:定位范围

例如:

接口错误:

检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
浏览器



网络



服务器



数据库

第三步:观察状态

工具:

1
2
3
4
5
6
7
Console

Network

Debugger

日志

第四步:缩小范围

找到:

1
状态第一次错误的位置

十四、性能问题初步判断

接口慢:

看:

1
2
3
Waiting

Download

Waiting高:

可能:

1
2
3
4
5
6
7
服务器处理慢

数据库慢

缓存慢

第三方接口慢

Download高:

可能:

1
2
3
返回数据大

网络慢

CPU高:

可能:

1
同步计算阻塞主线程

CPU低:

接口慢:

可能:

1
IO等待

十五、当前学习路线

已学习:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
浏览器模型



HTTP



Socket



Event Loop



Debugger



Call Stack



Scope



Async Stack

下一步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
日志调试



服务器排查



性能分析



线上故障定位



并发问题



内存问题