文章目录练手准备知识点1var 为什么没有块级作用域题目1先思考 —— 自己想 30 秒解析再看一个为什么 ES6 要出 let知识点2作用域链是怎么找变量的题目2解析第一步第二步再看这个再看经典面试题原因创建阶段今日核心口诀var作用域链变量提升的本质今日习题菜鸟回答 —— 这里VO、AO菜鸟没搞清楚后面就清楚了GPT 批改你的答案原因你写的执行过程第一步全局执行上下文GEC进入 fn执行阶段整个过程画出来创建全局调用 fn加练题目第一题第二题难度1菜鸟回答 —— 这里AO、GO也没有很准确GPT 批改第一题真正执行过程全局创建阶段全局执行阶段调用 fn执行第二题执行过程全局创建阶段全局执行阶段调用 fn第一次输出执行 if第二次输出但是这里有个细节你的描述有一点问题创建阶段执行 if 后最终一题菜鸟回答 —— 这里AO、GO菜鸟就比较明确了GPT 批改你的答案输出原因唯一的小问题创建阶段执行真正的执行过程第一步创建 GO第二步执行 GO第三步进入 fn创建 AO先处理函数声明再处理 var第四步执行代码这道题最重要的结论接第一天直接让GPT每日训练我今天一上来GPT先给我科普了一波练手准备知识点1var为什么没有块级作用域题目1if(true){vara1;}console.log(a);先思考 —— 自己想 30 秒输出什么A1BundefinedC报错解析因为var只有全局作用域函数作用域没有if 作用域for 作用域while 作用域switch 作用域所以if(true){vara1;}实际上等价于vara;if(true){a1;}console.log(a);// 1答案1再看一个for(vari0;i3;i){}console.log(i);输出很多人刚工作时会觉得i应该只存在于for里面。但实际上var不会被for创建的作用域限制等价于vari;for(i0;i3;i){}console.log(i);答案3为什么 ES6 要出let因为var太容易产生污染if(true){varuser{};}for(vari0;i100;i){}console.log(user);console.log(i);所有变量全部泄露到外面。所以let拥有块级作用域if(true){leta1;}console.log(a);直接ReferenceError知识点2作用域链是怎么找变量的题目2vara1;functionfn(){console.log(a);}fn();输出什么A1BundefinedC报错解析JS 查找变量遵循当前作用域找不到 → 去父作用域 → 一直向上找到全局作用域。这个链条叫作用域链Scope Chain图Global └── fn执行functionfn(){console.log(a);}步骤第一步先找fn内部有没有a。第二步沿作用域链向上Global发现vara1所以答案1再看这个vara1;functionfn(){vara2;console.log(a);}fn();作用域Global └── fn └── a2查找console.log(a)先找当前作用域vara2已经找到不会继续向上。答案2再看经典面试题vara1;functionfn(){console.log(a);vara2;}fn();很多人会答 1实际上输出undefined原因创建阶段fnAO{a:undefined}作用域Global └──fn(aundefined)执行console.log(a);先找当前作用域aundefined已经找到不会再去全局找a1所以undefined今日核心口诀var只有函数作用域 没有块级作用域作用域链当前作用域找 找到立即停止 找不到向父级找 一直找到全局变量提升的本质先创建作用域这个昨天有深入的理解 再创建变量 最后执行代码今日习题vara1;functionfn(){console.log(a);if(false){vara2;}}fn();菜鸟回答 —— 这里VO、AO菜鸟没搞清楚后面就清楚了输出undefined原因var没有if的作用域会被提升出去所以a就是undefined因为函数里面有所以也不会往外找 执行过程 第一步 外部AO a1VO fn 第二步AO aundefined后续也不会赋值GPT 批改非常好这题你已经抓住核心了属于理解到了变量提升和作用域链的交汇点。但有两个小细节需要纠正。你的答案undefined✅ 正确。原因你说var没有if的作用域会被提升出去所以a就是undefined因为函数里面有所以也不会往外找✅ 思路正确。可以再专业一点var a虽然写在if(false)里面但var没有块级作用域因此会提升到fn的函数作用域顶部并初始化为undefined。执行console.log(a)时当前作用域已经存在变量a值为undefined因此不会继续沿作用域链向上查找全局的a。你写的执行过程你写第一步外部AO:a1VO:fn这里概念混了一点。实际上全局执行上下文GEC创建阶段GO{a:undefined,fn:functionfn(){}}执行阶段GO{a:1,fn:functionfn(){}}进入 fn创建阶段if(false){vara2;}虽然不会执行a2但是vara仍然会提升。所以AO{a:undefined}执行阶段执行console.log(a);JS 查找先看AO{a:undefined}已经找到a。于是console.log(undefined);结束。因为if(false)条件为假a2永远不会执行。最终AO{a:undefined}整个过程画出来创建全局GO├── a:undefined└──fn:function执行GO├── a:1└──fn:function调用 fn创建 AOAO(fn)└── a:undefined作用域链GO└──AO(fn)执行console.log(a)查找过程AO(fn)└── a:undefined← 找到了停止查找输出undefined不会再去GO└── a:1加练题目第一题vara1;functionfn(){if(true){vara2;}console.log(a);}fn();按照格式回答输出AO 执行过程第二题难度1vara1;functionfn(){console.log(a);if(true){vara2;}console.log(a);}fn();按照格式回答输出AO 第一次查找 第二次查找菜鸟回答 —— 这里AO、GO也没有很准确第一题 输出2AO fn aundefined但是后面iftrue了所以a2执行过程 第一步GO a1fn 第二步AO aundefined但是后面iftrue了所以a2第二题 输出undefined2AO fn aundefined但是后面iftrue了所以a2第一次查找 aundefined第二次查找a2GPT 批改第一题vara1;functionfn(){if(true){vara2;}console.log(a);}fn();你的答案输出2✅ 完全正确。真正执行过程全局创建阶段GO{a:undefined,fn:functionfn(){}}全局执行阶段GO{a:1,fn:functionfn(){}}调用 fn创建 AOAO{a:undefined}作用域链GO└──AO(fn)执行执行if(true){a2;}此时AO{a:2}继续console.log(a);查找AO(fn)└── a:2输出 2第二题vara1;functionfn(){console.log(a);if(true){vara2;}console.log(a);}fn();你的答案输出undefined2✅ 完全正确。执行过程全局创建阶段GO{a:undefined,fn:functionfn(){}}全局执行阶段GO{a:1,fn:functionfn(){}}调用 fn创建 AOAO{a:undefined}第一次输出执行console.log(a)查找AO(fn)└── a:undefined已经找到停止向上查找。输出undefined执行 ifif(true){a2;}此时AO{a:2}第二次输出执行console.log(a)查找AO(fn)└── a:2输出 2但是这里有个细节你的描述有一点问题你写AO fn aundefined但是后面iftrue了所以a2严格来说应该拆成两个阶段。创建阶段AO{a:undefined}执行 if 后AO{a:2}因为AO 是在进入函数的时候一次性创建好的不会因为执行 if 再重新创建。这是以后阅读 Vue 源码非常重要的思维。很多人脑子里的模型是进入if↓ 创建变量这是错的真正模型进入函数 ↓ 创建AO↓ 收集所有var↓ 初始化undefined↓ 开始执行代码 ↓ 给变量赋值最终一题vara1;functionfn(){console.log(a);vara2;functiona(){}console.log(a);}fn();请按格式回答GOAO 第一次输出 第二次输出 原因菜鸟回答 —— 这里AO、GO菜鸟就比较明确了GO{a:1fn:functionfn(){}}AO{a:functiona(){}a:2}第一次输出functiona(){}第二次输出2原因昨天的函数整体提升AO中一开始a被赋值为functiona(){}后面的var声明的变量a因为已经有a了所以不会在创建a所以第一次是functiona(){}后面执行vara2将a赋值为了2所以第二次是2functiona提升了所以这里不会再运行GPT 批改非常好这次已经接近满分答案了。你已经开始形成执行上下文的运行模型了。我只纠正最后一个容易混淆的点。你的答案输出functiona(){}2✅ 正确原因functiona 提升 ↓AO.afunctiona(){}↓vara 发现已经存在 ↓ 不会重新创建 ↓ 第一次输出函数 ↓ 执行 a2↓ 第二次输出2✅ 完全正确。唯一的小问题你写AO{a:functiona(){}a:2}严格来说这是执行完成后的 AO不是创建阶段的 AO。应该拆开写创建阶段AO{a:functiona(){}}执行执行vara2;实际上a2;此时AO{a:2}真正的执行过程第一步创建 GOGO{a:undefined,fn:functionfn(){}}第二步执行 GOGO{a:1,fn:functionfn(){}}第三步进入 fn创建 AO扫描函数体先处理函数声明functiona(){}得到AO{a:functiona(){}}再处理 varvara;发现 a 已经存在什么都不做AO{a:functiona(){}}第四步执行代码执行console.log(a);输出functiona(){}然后vara2;等价于a2;AOAO{a:2}最后console.log(a);输出2这道题最重要的结论在同一个作用域functiona(){}vara;等价于AO{a:functiona(){}}不是AO{a:undefined}也不是AO{a:functiona(){},a:undefined}因为对象属性不可能同时存在两个同名 key。