GPT训练我的第三天,明白了应该咋说满分回答!😕😕😕

继续

接前两天的文章:

  1. 直接让GPT每日训练我!!!😕😕😕
  2. GPT训练我的第二天,我表示不过如此!!!😕😕😕

回顾

昨天你已经理解了:

  • 函数提升优先于 var 变量提升
  • GO/AO(全局对象/执行上下文对象)
  • 创建阶段和执行阶段

今天继续推进两个核心知识点:

  1. 执行上下文(Execution Context)完整流程
  2. 作用域链(Scope Chain)到底是怎么找变量的

今日训练

第一题:执行上下文到底经历了什么?

看代码:

js 复制代码
var a = 1

function fn() {
  var b = 2
  console.log(a)
  console.log(b)
}

fn()

请不要运行,按照下面顺序回答:

第一步:进入全局执行上下文(GO)

GO 里面有什么?

写出:

js 复制代码
GO = {

}

第二步:执行到 fn()

创建函数执行上下文(AO)。

写出:

js 复制代码
AO = {

}

第三步:执行:

js 复制代码
console.log(a)

JS 引擎是:

  1. 去 AO 找?
  2. 去 GO 找?
  3. 还是直接全局找?

请写出完整查找过程。


第二题:作用域链

再看:

js 复制代码
var a = 1

function fn() {
  var b = 2

  function test() {
    var c = 3
    console.log(a)
    console.log(b)
    console.log(c)
  }

  test()
}

fn()

不要运行,请回答:

问题1

执行:

js 复制代码
console.log(c)

变量在哪里找到?

问题2

执行:

js 复制代码
console.log(b)

查找路径是什么?

例如:

js 复制代码
AO(test)
↓
AO(fn)
↓
GO

问题3

执行:

js 复制代码
console.log(a)

查找路径是什么?

小任务(非常重要)

从现在开始,每看一个函数,都先在脑子里画:

js 复制代码
GO
 ↓
AO(fn)
 ↓
AO(test)

以后学:

  • 闭包
  • this
  • call/apply/bind
  • Vue 响应式源码
  • Pinia
  • React Hooks

全部建立在这套执行上下文和作用域链模型之上。

菜鸟回答 ------ 这里菜鸟还是没有掌握满分回答的精髓

js 复制代码
第一题:
GO:{
  a:1,
  fn:function fn() {}
}

AO:{
  a:1,
  b:2
}

a 会现在 AO 找,发现找不到所以去 GO 找到了a:1,所以console.log(a) 会输出1

第二题:
console.log(c) 直接在test的AO中找,并找到了,输出3
console.log(b) 先在test的AO中找,找不到,就去fn的AO找,找到了,输出2
console.log(a) 先在test的AO中找,找不到,就去fn的AO找,也找不到,去GO找,找到了,输出1

GPT 批改

第一题批改

代码:

js 复制代码
var a = 1

function fn() {
  var b = 2
  console.log(a)
  console.log(b)
}

fn()

GO

你写的是:

js 复制代码
GO = {
  a: 1,
  fn: function fn() {}
}

✅ 最终执行完成后是对的。

但如果按照执行上下文的完整流程:

创建阶段

js 复制代码
GO = {
  a: undefined,
  fn: function fn() {}
}

执行阶段

执行:

js 复制代码
a = 1

变成:

js 复制代码
GO = {
  a: 1,
  fn: function fn() {}
}

AO

你写的是:

js 复制代码
AO = {
  a: 1,
  b: 2
}

这里错了一点。

创建阶段

进入 fn

js 复制代码
AO = {
  b: undefined
}

因为:

js 复制代码
function fn() {
  var b = 2
}

函数里面只有:

js 复制代码
var b

并没有:

js 复制代码
var a

所以 a 不会出现在 AO 中。

执行阶段

执行:

js 复制代码
b = 2

变成:

js 复制代码
AO = {
  b: 2
}

查找 a

你写的:

AO 找不到 → GO 找到

完全正确。

实际上 JS 引擎干的事情类似:

js 复制代码
AO(fn)
  ↓ 没有 a
GO
  ↓ 找到 a = 1
返回 1

第二题批改

代码:

js 复制代码
var a = 1

function fn() {
  var b = 2

  function test() {
    var c = 3
    console.log(a)
    console.log(b)
    console.log(c)
  }

  test()
}

fn()

console.log(c)

你:

直接在 test 的 AO 找到,输出 3

✅ 正确。

js 复制代码
AO(test)
  ↓
找到 c = 3

console.log(b)

你:

test 的 AO 没有 → fn 的 AO 找到

✅ 正确。

js 复制代码
AO(test)
  ↓
AO(fn)
  ↓
找到 b = 2

console.log(a)

你:

test 的 AO 没有 → fn 的 AO 没有 → GO 找到

✅ 完全正确。

js 复制代码
AO(test)
  ↓
AO(fn)
  ↓
GO
  ↓
找到 a = 1

今日加练(闭包预热)

不要运行:

js 复制代码
var a = 1

function fn() {
  var b = 2

  function test() {
    console.log(a)
    console.log(b)
  }

  return test
}

var f = fn()
f()

问题1

执行完:

js 复制代码
var f = fn()

以后:

js 复制代码
fn 的 AO

会不会销毁?


问题2

执行:

js 复制代码
f()

时:

js 复制代码
console.log(b)

是怎么找到 b 的?

菜鸟回答 ------ 这里已经大致知道GPT想要什么答案了

js 复制代码
问题1:不会


问题2:
解析阶段:
GO: {
 a:undefined,
 f:function fn() {}
}

执行:
GO: {
 a:1,
 f:function fn() {}
}

解析阶段:
AO(fn) {
  b:undefined,
  test:function test() {}
}

执行阶段:
AO(fn) {
  b:2,
  test:function test() {}
}

解析阶段:
AO(test) {}

执行阶段:
b找不到,去AO(fn)中找,找到了,所以是2
a找不到,去AO(fn)中找,还是找不到,去GO中找,所以是1

GPT 解析

问题1:fn 的 AO 会不会销毁?

你回答:

不会

✅ 正确。

但是原因你还没有说出来。

因为:

js 复制代码
var f = fn()

实际上:

js 复制代码
f === test

也就是:

js 复制代码
var f = test

test 函数里面:

js 复制代码
console.log(b)

需要访问:

js 复制代码
AO(fn)

JS 引擎发现:

虽然 fn 执行结束了,但是还有一个函数(test)在引用它里面的变量。

所以:

js 复制代码
AO(fn)
    ↑
test 引用了它

因此:

js 复制代码
AO(fn) 不会被垃圾回收(GC)

这就是闭包产生的根本原因。


问题2:你的 GO 写错了

代码:

js 复制代码
var a = 1

function fn() {
  var b = 2

  function test() {
    console.log(a)
    console.log(b)
  }

  return test
}

var f = fn()
f()

你写:

js 复制代码
GO = {
  a: undefined,
  f: function fn() {}
}

这里错了。

创建阶段

js 复制代码
GO = {
  a: undefined,
  fn: function fn() {},
  f: undefined
}

因为:

js 复制代码
var a
function fn(){}
var f

都会提升。

执行阶段

执行:

js 复制代码
a = 1
js 复制代码
GO = {
  a: 1,
  fn: function fn() {},
  f: undefined
}

执行:

js 复制代码
f = fn()

此时:

js 复制代码
fn()

返回:

js 复制代码
test

所以:

js 复制代码
GO = {
  a: 1,
  fn: function fn() {},
  f: function test() {}
}

AO(fn)

创建阶段:

js 复制代码
AO(fn) = {
  b: undefined,
  test: function test() {}
}

执行阶段:

js 复制代码
AO(fn) = {
  b: 2,
  test: function test() {}
}

AO(test)

创建阶段:

js 复制代码
AO(test) = {}

执行阶段:

js 复制代码
AO(test) = {}

因为:

js 复制代码
function test() {
  console.log(a)
  console.log(b)
}

里面没有:

  • var
  • function
  • 参数

所以 AO 为空。


查找 b

执行:

js 复制代码
console.log(b)

查找过程:

js 复制代码
AO(test)
   ↓ 没有 b
AO(fn)
   ↓ 找到 b = 2
返回 2

查找 a

执行:

js 复制代码
console.log(a)

查找过程:

js 复制代码
AO(test)
   ↓ 没有 a
AO(fn)
   ↓ 没有 a
GO
   ↓ 找到 a = 1
返回 1

但是最关键的是这一句

很多人会以为:

js 复制代码
f()

执行时:

js 复制代码
AO(test)
↓
GO

因为:

js 复制代码
f()

是在全局调用的,这是错误的!


真正的作用域链

作用域链由:

函数定义的位置决定(词法作用域)

不是:

函数调用的位置决定

所以:

js 复制代码
function fn() {
  var b = 2

  function test() {
    console.log(b)
  }

  return test
}

test 出生的时候:

js 复制代码
GO
 ↓
AO(fn)
 ↓
AO(test)

这个链条已经固定了。

即使:

js 复制代码
var f = fn()

f()

甚至:

js 复制代码
setTimeout(f)
obj.xxx = f
window.abc = f

调用位置变了无数次。

但是:

js 复制代码
AO(test)
 ↓
AO(fn)
 ↓
GO

永远不会变。

验收题(非常重要)

不要运行:

js 复制代码
var a = 100

function fn() {
  var a = 200

  function test() {
    console.log(a)
  }

  return test
}

var f = fn()

a = 300

f()

请你推导:

  1. 输出多少?
  2. 查找路径是什么?

菜鸟回答 ------ 接近满分(所以就不给GPT批改了)

js 复制代码
解析阶段:
GO: {
  a: undefined,
  fn: function fn() {},
  f: undefined
}

执行阶段:
a = 100

GO: {
  a: 100,
  fn: function fn() {},
  f: undefined
}

f = fn()

GO: {
  a: 100,
  fn: function fn() {},
  f: function test() {}
}

a = 300

GO: {
  a: 300,
  fn: function fn() {},
  f: function test() {}
}

解析阶段
AO(fn): {
  a:undefined,
  test: function test() {}
}

执行阶段:
a = 200

AO(fn): {
  a:200,
  test: function test() {}
}

解析阶段:
AO(test): {}

执行阶段:
AO(test)找不到a,去AO(fn)找到a,所以是200
相关推荐
像我这样帅的人丶你还2 小时前
Java 后端详解(四):分页与搜索
java·javascript·后端
摸着石头过河的石头2 小时前
前端多仓库管理:从混乱到有序的进化之路
前端
星栈2 小时前
写 Dioxus Demo 不难,难的是把它写成项目
前端·rust·前端框架
labixiong2 小时前
还原一个完整符合规范的 Promise(二)
前端·javascript
时光足迹2 小时前
腾讯云 TRTC UniApp SDK 从入门到上线
前端·vue.js·uni-app
时光足迹2 小时前
uni-app 里把加密视频嵌入页面播放?我分析了 4 种方案,只有 1 种接近完美
前端·vue.js·uni-app
To_OC3 小时前
万字解析《JS 语言精粹》之第五章:继承 5 大核心精髓(JS 原型核心)
前端·javascript·代码规范
时光足迹3 小时前
极光推送全攻略(上):被iOS证书折磨了三天,我写了一份前端也能看懂的避坑指南
前端·ios·uni-app
DyLatte3 小时前
AI 时代,最危险的不是被替代,而是努力不沉淀
前端·后端·程序员