ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

var、let、const区别,不要再混淆变量提升

var、let、const区别,不要再混淆变量提升 说实话JavaScript 里的变量声明看起来简单但真到了写代码的时候尤其是涉及到var、let和const的选择不少工作了一两年的朋友可能还在凭感觉用。我刚开始学的时候总觉得“能跑就行”直到有一次因为var的变量提升在循环里出了个很难排查的Bug才痛下决心要把这哥仨彻底搞清楚。今天咱们不背概念直接从底层机制和实际场景出发把这层窗户纸捅破。看完这篇以后面试官再问你不仅能答出区别还能顺便讲讲暂时性死区和块级作用域直接拿捏。1. 变量提升Hoistingvar 的“历史遗留问题”先来看一段代码你觉得会输出什么console.log(a); // undefined var a 10;在很多语言里这肯定直接报错“未定义”。但在 JavaScript ES5里它居然输出undefined。这就是变量提升在作祟。JavaScript 引擎在编译阶段会把var声明的变量和function声明挪到当前作用域的顶部。所以上面的代码在引擎眼里其实是这样的var a; // 声明被提升初始值为 undefined console.log(a); // undefined a 10; // 赋值留在原地var就这么个毛病声明提升了但赋值没动。如果你在赋值前访问它拿到的就是undefined而不是报错。这很容易导致一些隐蔽的 bug比如变量名拼错了结果拿到的却是undefined程序还继续往下跑最后在某个角落炸掉。2. 块级作用域let 和 const 带来的“新世界”let和const是 ES6 引入的它们解决了var最大的痛点——没有块级作用域。以前用var只有全局作用域和函数作用域。比如if (true) { var b 20; } console.log(b); // 20变量污染了外部这在for循环里特别坑for (var i 0; i 3; i) { setTimeout(() console.log(i), 100); } // 输出 3, 3, 3因为i是全局的循环结束后i变成 3三个定时器都打印 3。换成let试试for (let i 0; i 3; i) { setTimeout(() console.log(i), 100); } // 输出 0, 1, 2let是块级作用域每次循环都会生成一个新的块把i“锁”在里面。所以定时器拿到的每个i都是独立的副本。const也一样声明的变量只在当前大括号{}内有效。3. 暂时性死区TDZlet 和 const 的“严格模式”你可能听过“let没有变量提升”这个说法。其实严格来说let和const也有提升但引擎对它们有一个限制在声明之前你不能访问它们。console.log(c); // ReferenceError: Cannot access c before initialization let c 30;从进入当前作用域开始到变量声明完成之前这块区域就叫暂时性死区Temporal Dead Zone, TDZ。let x 10; if (true) { // 这里就进入了 TDZ console.log(x); // ReferenceError let x 20; // 声明在此 }哪怕外面有个同名的x在块里面声明之前这个x也是访问不到的。这其实是个好事强制我们把变量声明放在使用之前代码更健壮。4. const 的“不变”陷阱很多人以为const定义的是常量不能修改。这句话对了一半。const PI 3.14; PI 3.1415; // TypeError: Assignment to constant variable.基本类型数字、字符串、布尔确实不能改。但const真正保护的是变量的引用地址而不是值本身。const person { name: 张三 }; person.name 李四; // 允许因为引用没变 person {}; // 报错尝试改变引用所以如果你用const声明一个对象你是可以修改对象内部属性的但不能把整个对象重新赋值。5. 实际开发中到底怎么选这个问题我问过很多同事总结下来有一套比较实用的“心法”默认用const除非你明确知道这个变量后面需要重新赋值否则一律const。这能防止意外篡改也让代码的意图更清晰。需要重新赋值时用let比如循环计数器、累加变量、或者后续逻辑会改变的变量。尽量别用var在现在的工程化项目里var基本可以退休了。除非你要兼容远古浏览器比如 IE11否则没必要再用它。我自己的习惯是写 React 组件时导入的模块用const状态用let永远不用var。6. 一张图帮你理清全局对象还有一个冷知识在全局作用域下var声明的变量会成为window浏览器或globalNode的属性而let和const不会。var a 1; let b 2; console.log(window.a); // 1 console.log(window.b); // undefined这其实也解释了为什么var容易造成全局污染。如果你正在写一个工具库一不小心用var声明了个公共变量很可能就把别人的同名变量覆盖了。总结一下特性varletconst作用域全局/函数作用域块级作用域块级作用域变量提升是初始undefined是但存在 TDZ不初始化是但存在 TDZ不初始化重复声明允许不允许不允许修改值允许允许基本类型不可改引用类型可改属性挂载到window是否否其实这背后的核心逻辑就是 JavaScript 在设计上越来越严谨和安全。var的宽松设计在早期确实方便但带来的维护成本太高。let和const的引入本质上是在帮你养成好习惯。下次面试官再问这个问题你可以先抛出变量提升和 TDZ 的区别再结合for循环的经典例子说明块级作用域的好处最后提一嘴const的引用赋值。这一套组合拳下来相信对方心里已经有数了。好了关于这三者的区别就聊到这儿。如果你在项目中因为变量声明踩过什么坑也欢迎在评论区分享出来大家一起避避雷。觉得有用的话点个赞再走~
返回列表