Debug 指南 - 现象归因

408 阅读2分钟

我把 debug 定义为这样一个过程:当表现不符合预期时,寻找导致当前现象的最根本原因。
这里强调最根本原因,即不正常的源头,治根,而非让其表现暂时正常。

debug 的过程可谓艰辛,猜想 -> 验证 不断的循环中,也许找到了原因,但是回过头,发现如果第一次就去试验这个猜想,是不是就不用花这么长时间呢?有些猜想明显在当前情景下不成立,如果多考虑下,就不会花这么多时间去试错了... 更令人沮丧的是,debug到最后,迷失方向以至放弃。

这里试图归纳一套有限可执行步骤,跟谁这个步骤,能保证触及bug根源,并且多数时候是以最短的路径。你有这样的安全感 —— 不存在找不到的bug。

在给出这个步骤前,我会先分解如此设计的原因,并落到两个分类:通用策略 和 前端领域特定。
理解设计缘由,才能把步骤执行的到位。

现象归因:通用策略

由表及里

这是最直觉的策略:先找导致现象的最直接原因,然后找原因的原因,直到最初的原因。这最初的原因一般是我们自己写的代码。

具体步骤:

  1. 需找和异常现象关联的数据表示
  2. 追寻数据产生错误的源头
  3. 修正源头

现象异常的常见原因

  1. 计算次序有误,在保存自身状态的应用中,不同的计算次序往往会产生不同的结果。这个往往隐蔽且不易发现
  2. 计算逻辑。这个很容易理解了。

前端领域特定

log

打印是常用的手段,用于检查运行到某个位置时变量值。但在 JS 语言中有一个陷阱:

  1. 如果打印的是 primary value,没什么问题
  2. 如果打印的是对象,打印的并不是那一刻的快照,而是变量的引用,所以你看到只会是最终值,而不是运行到那个位置的值。所以总是使用 JSON.stringify 去打印 log。

Object.defineProperty

这是一个直捣错误源头的办法。上面说过,现象异常对应着某个或几个变量值异常,寻找异常源头,就是沿着异常值计算的过程。如果我们在该变量赋异常值时中断,观察调用栈,往往很容易找到源头。

一个例外,如果调用栈中有大量异步调用,并且存在收集异步任务的行为,这个办法的效果不是很好,因为异步任务的两端在调用栈信息里无法对应起来。

通过一个例子来理解: juejin.cn/post/684490…