GPT训练我的第三天,明白了应该咋说满分回答!
文章目录
- 回顾
- 今日训练
- 第一题:执行上下文到底经历了什么?
- 第一步:进入全局执行上下文(GO)
- 第二步:执行到 `fn()` 时
- 第三步:执行:
- 第二题:作用域链
- 问题1
- 问题2
- 问题3
- 小任务(非常重要)
- 菜鸟回答 —— 这里菜鸟还是没有掌握满分回答的精髓
- GPT 批改
- 第一题批改
- GO
- 创建阶段
- 执行阶段
- AO
- 创建阶段
- 执行阶段
- 查找 `a`
- 第二题批改
- console.log(c)
- console.log(b)
- console.log(a)
- 今日加练(闭包预热)
- 问题1
- 问题2
- 菜鸟回答 —— 这里已经大致知道GPT想要什么答案了
- GPT 解析
- 问题1:`fn` 的 AO 会不会销毁?
- 问题2:你的 GO 写错了
- 创建阶段
- 执行阶段
- AO(fn)
- AO(test)
- 查找 b
- 查找 a
- 但是最关键的是这一句
- 真正的作用域链
- 验收题(非常重要)
- 菜鸟回答 —— 接近满分(所以就不给GPT批改了)
接前两天的文章:
- 直接让GPT每日训练我!!!😕😕😕
- GPT训练我的第二天,我表示不过如此!!!😕😕😕
回顾
昨天你已经理解了:
- 函数提升优先于
var变量提升 GO/AO(全局对象/执行上下文对象)- 创建阶段和执行阶段
今天继续推进两个核心知识点:
- 执行上下文(Execution Context)完整流程
- 作用域链(Scope Chain)到底是怎么找变量的
今日训练
第一题:执行上下文到底经历了什么?
看代码:
vara=1functionfn(){varb=2console.log(a)console.log(b)}fn()请不要运行,按照下面顺序回答:
第一步:进入全局执行上下文(GO)
GO 里面有什么?
写出:
GO={}第二步:执行到fn()时
创建函数执行上下文(AO)。
写出:
AO={}第三步:执行:
console.log(a)JS 引擎是:
- 去 AO 找?
- 去 GO 找?
- 还是直接全局找?
请写出完整查找过程。
第二题:作用域链
再看:
vara=1functionfn(){varb=2functiontest(){varc=3console.log(a)console.log(b)console.log(c)}test()}fn()不要运行,请回答:
问题1
执行:
console.log(c)变量在哪里找到?
问题2
执行:
console.log(b)查找路径是什么?
例如:
AO(test)↓AO(fn)↓GO问题3
执行:
console.log(a)查找路径是什么?
小任务(非常重要)
从现在开始,每看一个函数,都先在脑子里画:
GO↓AO(fn)↓AO(test)以后学:
- 闭包
- this
- call/apply/bind
- Vue 响应式源码
- Pinia
- React Hooks
全部建立在这套执行上下文和作用域链模型之上。
菜鸟回答 —— 这里菜鸟还是没有掌握满分回答的精髓
第一题:GO:{a:1,fn:functionfn(){}}AO:{a:1,b:2}a 会现在AO找,发现找不到所以去GO找到了a:1,所以console.log(a)会输出1第二题: console.log(c)直接在test的AO中找,并找到了,输出3console.log(b)先在test的AO中找,找不到,就去fn的AO找,找到了,输出2console.log(a)先在test的AO中找,找不到,就去fn的AO找,也找不到,去GO找,找到了,输出1GPT 批改
第一题批改
代码:
vara=1functionfn(){varb=2console.log(a)console.log(b)}fn()GO
你写的是:
GO={a:1,fn:functionfn(){}}✅ 最终执行完成后是对的。
但如果按照执行上下文的完整流程:
创建阶段
GO={a:undefined,fn:functionfn(){}}执行阶段
执行:
a=1变成:
GO={a:1,fn:functionfn(){}}AO
你写的是:
AO={a:1,b:2}这里错了一点。
创建阶段
进入fn:
AO={b:undefined}因为:
functionfn(){varb=2}函数里面只有:
varb并没有:
vara所以a不会出现在 AO 中。
执行阶段
执行:
b=2变成:
AO={b:2}查找a
你写的:
AO 找不到 → GO 找到
完全正确。
实际上 JS 引擎干的事情类似:
AO(fn)↓ 没有 aGO↓ 找到 a=1返回1第二题批改
代码:
vara=1functionfn(){varb=2functiontest(){varc=3console.log(a)console.log(b)console.log(c)}test()}fn()console.log©
你:
直接在 test 的 AO 找到,输出 3
✅ 正确。
AO(test)↓ 找到 c=3console.log(b)
你:
test 的 AO 没有 → fn 的 AO 找到
✅ 正确。
AO(test)↓AO(fn)↓ 找到 b=2console.log(a)
你:
test 的 AO 没有 → fn 的 AO 没有 → GO 找到
✅ 完全正确。
AO(test)↓AO(fn)↓GO↓ 找到 a=1今日加练(闭包预热)
不要运行:
vara=1functionfn(){varb=2functiontest(){console.log(a)console.log(b)}returntest}varf=fn()f()问题1
执行完:
varf=fn()以后:
fn 的AO会不会销毁?
问题2
执行:
f()时:
console.log(b)是怎么找到b的?
菜鸟回答 —— 这里已经大致知道GPT想要什么答案了
问题1:不会 问题2: 解析阶段:GO:{a:undefined,f:functionfn(){}}执行:GO:{a:1,f:functionfn(){}}解析阶段:AO(fn){b:undefined,test:functiontest(){}}执行阶段:AO(fn){b:2,test:functiontest(){}}解析阶段:AO(test){}执行阶段: b找不到,去AO(fn)中找,找到了,所以是2a找不到,去AO(fn)中找,还是找不到,去GO中找,所以是1GPT 解析
问题1:fn的 AO 会不会销毁?
你回答:
不会
✅ 正确。
但是原因你还没有说出来。
因为:
varf=fn()实际上:
f===test也就是:
varf=test而test函数里面:
console.log(b)需要访问:
AO(fn)JS 引擎发现:
虽然 fn 执行结束了,但是还有一个函数(test)在引用它里面的变量。
所以:
AO(fn)↑ test 引用了它因此:
AO(fn)不会被垃圾回收(GC)这就是闭包产生的根本原因。
问题2:你的 GO 写错了
代码:
vara=1functionfn(){varb=2functiontest(){console.log(a)console.log(b)}returntest}varf=fn()f()你写:
GO={a:undefined,f:functionfn(){}}这里错了。
创建阶段
GO={a:undefined,fn:functionfn(){},f:undefined}因为:
varafunctionfn(){}varf都会提升。
执行阶段
执行:
a=1GO={a:1,fn:functionfn(){},f:undefined}执行:
f=fn()此时:
fn()返回:
test所以:
GO={a:1,fn:functionfn(){},f:functiontest(){}}AO(fn)
创建阶段:
AO(fn)={b:undefined,test:functiontest(){}}执行阶段:
AO(fn)={b:2,test:functiontest(){}}AO(test)
创建阶段:
AO(test)={}执行阶段:
AO(test)={}因为:
functiontest(){console.log(a)console.log(b)}里面没有:
- var
- function
- 参数
所以 AO 为空。
查找 b
执行:
console.log(b)查找过程:
AO(test)↓ 没有 bAO(fn)↓ 找到 b=2返回2查找 a
执行:
console.log(a)查找过程:
AO(test)↓ 没有 aAO(fn)↓ 没有 aGO↓ 找到 a=1返回1但是最关键的是这一句
很多人会以为:
f()执行时:
AO(test)↓GO因为:
f()是在全局调用的,这是错误的!
真正的作用域链
作用域链由:
函数定义的位置决定(词法作用域)
不是:
函数调用的位置决定
所以:
functionfn(){varb=2functiontest(){console.log(b)}returntest}test出生的时候:
GO↓AO(fn)↓AO(test)这个链条已经固定了。
即使:
varf=fn()f()甚至:
setTimeout(f)obj.xxx=f window.abc=f调用位置变了无数次。
但是:
AO(test)↓AO(fn)↓GO永远不会变。
验收题(非常重要)
不要运行:
vara=100functionfn(){vara=200functiontest(){console.log(a)}returntest}varf=fn()a=300f()请你推导:
- 输出多少?
- 查找路径是什么?
菜鸟回答 —— 接近满分(所以就不给GPT批改了)
解析阶段:GO:{a:undefined,fn:functionfn(){},f:undefined}执行阶段: a=100GO:{a:100,fn:functionfn(){},f:undefined}f=fn()GO:{a:100,fn:functionfn(){},f:functiontest(){}}a=300GO:{a:300,fn:functionfn(){},f:functiontest(){}}解析阶段AO(fn):{a:undefined,test:functiontest(){}}执行阶段: a=200AO(fn):{a:200,test:functiontest(){}}解析阶段:AO(test):{}执行阶段:AO(test)找不到a,去AO(fn)找到a,所以是200