上篇我们讲到,讲的时候卡住的地方就是缺口。这一篇讲怎么把缺口补上,而且补得扎实。
很多人卡住之后的动作是:再搜一篇讲这个的文章看看。这是最顺手、也最没用的做法。你第一次看那篇没讲通,是因为它把难点跳过了;换一篇,大概率同样跳过,因为大多数博客都假设读者已经懂了那一步。你只是在不同的二手材料之间打转。
缺口长什么样
先把卡壳分个类,补法不一样:
黑盒型:只能说"它就是那样"。比如"为什么微任务比宏任务先执行",你答"规范规定的"。规范为什么这么规定,你不知道。这种缺口在"因果的起点"。 悬空型:讲 A 要靠 B,讲 B 要靠 C,你对 B、C 都是"好像懂"。整条链没一段踩实。这种缺口在"依赖的概念"上。 类比型:用比喻讲完,但比喻一较真就漏。比如"事件循环像排队叫号",可一问"那微任务为什么能插队",比喻接不上。这种缺口在"比喻之外的真实机制"。
三类都指向同一件事:你需要的是源头,不是另一篇二手讲解。
回到源头:程序员的三条近路
对程序员来说,源头比普通人近得多。普通人学一个概念,源头可能是论文或教科书;我们有三样更顺手的:
官方文档和 spec:语言特性、协议细节,规范里写得最准。MDN 讲"怎么做",但 ECMAScript 规范讲"为什么是这样"。讲不清"微任务为什么先执行",去规范里查 Jobs 和 Execution Context 的定义,比看第十篇博客管用。 源码:框架和运行时是开源的。讲不清"React 的 setState 为什么有时批量有时不",去读源码里 batchedUpdates 的边界,比猜靠谱。 最小复现:写 20 行代码验证你的假设。你以为"Promise 里的错误能被外层 try/catch 接住",写个小 demo 一跑,发现接不住,因为 microtask 已经脱离了同步调用栈——这一跑,比读三篇文章都清楚。
最小复现尤其值得强调。它把"我以为"变成"我看见"。很多时候你讲不清,不是因为不会,是因为从没真正验证过自己的理解,demo 一跑就露馅。
一个实战:事件循环为什么单线程还不卡
拿上篇那个"微任务为什么先执行"往下走。黑盒型的你,补法是去读 HTML 规范里 event loop processing model 那一段,看清"每次取一个宏任务,执行完立刻清空所有微任务"这条顺序。读到这里,因果就接上了:不是微任务优先级高,是规范规定宏任务之间必须先把微任务排干。
悬空型的你,可能连"宏任务是什么"都没讲清。那补法是先写 demo:
console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4'); // 输出:1 4 3 2
跑出来是 1 4 3 2,而不是直觉里的 1 2 3 4。这个结果和你讲出来的因果对不上,你就知道"同步代码先跑完,再清空微任务,再取下一个宏任务"这条链得重新理。demo 给了你一个可以对着纠偏的事实,比任何文章都硬。
工程取舍:不是每个缺口都值得追到源码
说回现实。你一天遇到十个讲不清的概念,不可能每个都去读规范、读源码。得有个判断:
这个理解要是错了,会不会在生产环境出 bug?会,就追到源码层。比如"事务隔离级别到底隔离了什么",搞错会在线上写脏数据,值得读 MySQL 文档。不会,就先记一句结论,标个"待深挖",以后用到再说。
我自己的做法是:凡是会左右代码正确性的理解,必须回到源头补实;纯属背景知识的,先放过。这样费曼学习法才可持续,不会变成每天泡在规范里出不来。
现在就能做的练习
找一个你上篇圈出来的"卡住句",别搜博客,直接做下面一件:去官方文档查那个特性的权威定义,或者写 20 行代码验证你的假设。把跑出来的结果和你的直觉对一下,不一致的地方,就是你刚补上的缺口。