
一、前言前面我们学完了数组全套高阶 APImap/filter/reduce解决了数据处理、页面渲染、数据汇总的实战问题。从本篇开始我们深入 JavaScript底层核心机制。作用域、变量提升是 JS 最核心、面试必考、新手最容易懵的底层知识点。绝大多数 JS 诡异报错、变量 undefined、变量冲突、赋值失效问题全部源于这两个机制。本篇不堆空洞理论全程搭配分段打印测试案例逐行代码执行、逐句原理解析复制代码即可运行彻底搞懂作用域规则和变量提升底层逻辑。二、什么是作用域大白话终极解释作用域变量的「有效使用范围」简单来说变量不是写完整个页面都能用它有自己的管辖区域出了区域直接失效、报错。JS 中作用域分为三大类也是我们开发中唯一用到的所有场景全局作用域整个页面任意位置都能使用函数作用域仅在当前函数内部可用块级作用域{} 大括号内部生效if/for/while/代码块2.1 全局作用域所有直接写在最外层、不在函数/大括号内的变量都是全局变量全局作用域贯穿整个脚本任意位置可访问、可修改。// 全局变量 let name 前端学习; function test() { // 内部可以访问全局变量 console.log(name); } test(); // 外部也可以访问 console.log(name);2.2 函数作用域私有作用域在function 函数内部定义的变量属于函数作用域只能在函数内部使用外部完全无法访问这就是变量私有化。function fn() { // 仅函数内部生效 let num 100; console.log(num); // 正常打印 100 } fn(); // 报错num is not defined // console.log(num);核心规则函数外完全读不到函数内的变量私有隔离。2.3 块级作用域ES6 核心ES6 新增规则let / const 声明的变量在 if、for、while、单独 {} 大括号内会产生块级作用域出了大括号直接失效。重点var 没有块级作用域这是 var 被淘汰的核心原因。if (true) { // 块级变量 let msg hello; console.log(msg); // 内部正常使用 } // 报错 msg 未定义 // console.log(msg);三、作用域查找规则面试必考就近原则当代码需要读取一个变量时JS 会遵循从内到外、就近优先的查找规则先在当前作用域找变量找到直接用当前没有向外层父作用域查找层层向外冒泡直到全局作用域全局都没找到抛出变量未定义错误分段测试案例作用域就近查找// 全局变量 let a 10; function demo() { // 局部变量就近优先 let a 20; console.log(内部a, a); // 输出 20 } demo(); console.log(外部a, a); // 输出 10四、变量提升JS 最诡异的底层机制4.1 什么是变量提升JS 代码执行分为两步预解析阶段编译扫描全部代码提前提升变量、函数声明到当前作用域最顶部逐行执行阶段从上到下逐行运行代码变量提升所有var变量、函数声明会在代码执行前自动提升到「当前作用域的最顶端」。4.2 三种声明方式提升规则重中之重在深入规则之前先解答所有新手最大的疑惑变量自动提升到底有什么意义既然会制造undefinedbug为什么JS不取消很多人误以为「变量提升是设计漏洞」其实变量提升是JS底层必须存在的机制核心意义只有两个是JS代码能正常运行的关键1. 解决代码「前后依赖问题」让代码更灵活最典型的就是函数先调用、后定义。在实际开发中我们习惯把「业务逻辑代码写上面、工具函数写下面」。如果没有提升机制上面的代码调用下方的函数会直接报错代码必须严格从上到下顺序书写开发极其僵硬。有了提升机制JS预解析提前扫描所有函数、变量不在乎代码书写顺序提前注册标识符保证代码正常调用运行。2. 统一JS执行机制所有代码必须先预解析、再执行JS是解释型语言但不是逐行无脑执行必须先扫描全局所有变量、函数确认作用域归属、变量类型才能逐行执行赋值逻辑。变量提升本质不是bug是预解析的必然产物。预解析必须提前收集所有变量否则JS引擎读到一行未知变量时无法判断是「未定义报错」还是「下方后续声明」代码会彻底混乱无法运行。总结提升机制是为了让JS代码顺序自由、语法更灵活bug只是var语法的缺陷不是提升机制的问题。ES6推出let/const的暂时性死区就是保留提升优势、干掉提升缺陷的完美方案。var 变量存在变量提升提升后默认值undefined语法缺陷容易出现诡异undefinedlet / const存在提升但存在暂时性死区提升后不能先访问直接报错保留提升优势、修复bugfunction 函数全额提升可以先调用、后定义提升机制最大正面作用var 变量存在变量提升提升后默认值undefinedlet / const存在提升但存在暂时性死区提升后不能先访问直接报错function 函数全额提升可以先调用、后定义五、分段打印测试案例逐行吃透变量提升案例1var 变量提升经典诡异现象先打印后定义 console.log(num); var num 100; console.log(num);打印结果undefined → 100原理拆解预解析把var num提升到顶部默认值 undefined第一行打印变量已声明、未赋值输出 undefined执行赋值num 100第二行打印输出 100这就是新手最懵的变量没写却不报错打印 undefined的根本原因。案例2let/const 提升 暂时性死区console.log(str); let str 前端博客;结果直接报错 Cannot access str before initialization核心知识点let 也有提升但是提升后进入暂时性死区从作用域开头到变量定义这一段绝对不能访问变量访问直接报错。这也是为什么开发禁止用 var统一用 let/const杜绝 undefined 诡异问题语法更严谨。案例3函数提升最强提升// 先调用后定义完全正常 sayHello(); function sayHello() { console.log(函数提升生效); }函数声明会全额提升整个函数代码直接提到作用域顶部所以支持先调用、后定义这是函数独有的特性。案例4作用域 变量提升 综合坑点面试真题var a 10; function test() { console.log(a); var a 20; console.log(a); } test();最终打印结果undefined → 20逐行拆解原理必懂全局声明 a10进入函数预解析发现函数内有var a直接提升到函数顶部此时函数内的 a 优先级高于全局提升后值为 undefined第一次打印undefined赋值 a20第二次打印20这道题核心就是局部变量提升优先于全局变量。六、var / let / const 作用域提升对比var函数作用域、有变量提升、允许重复声明、易出bug、已淘汰let块级作用域、有提升暂时性死区、不允许重复声明、适合变量修改const块级作用域、有提升暂时性死区、不允许修改、适合常量固定值开发规范彻底弃用 var全部使用 let / const七、完整可运行测试 Demo一键运行自测下面是整合所有案例的完整页面打开控制台即可查看每一段打印结果适合反复调试巩固。!DOCTYPE html html langzh-CN head meta charsetUTF-8 title作用域与变量提升-完整测试案例/title /head body h3打开浏览器控制台查看打印结果/h3 script // 1. var变量提升测试 console.log(【1. var变量提升】); console.log(num); var num 100; console.log(num); // 2. 函数提升测试 console.log(\n【2. 函数提升】); sayHello(); function sayHello() { console.log(函数可以先调用后定义); } // 3. 作用域就近原则测试 console.log(\n【3. 作用域就近原则】); let a 10; function demo() { let a 20; console.log(局部a, a); } demo(); console.log(全局a, a); // 4. 经典面试题测试 console.log(\n【4. 综合面试题】); var b 10; function test() { console.log(函数内打印, b); var b 20; console.log(函数内赋值后, b); } test(); /script /body /html八、新手必踩坑总结变量提升只提升声明不提升赋值var 提升后默认 undefinedlet/const 有提升但存在暂时性死区先访问必报错彻底解决 var 诡异bug作用域查找永远就近优先局部变量覆盖全局变量函数提升优先级高于变量提升块级作用域仅 let/const 生效var 无块级隔离会造成变量污染