avaScript 这门语言,早期其实带点“赶工”的味道。当年为了快速给网页加点交互效果(比如做个幻灯片、操作下 DOM),一周左右就搞出了个原型,顺便蹭了波 Java 的热度,取名叫 JavaScript。它本质上是一门基于 ECMAScript 标准的弱类型动态语言。
但谁也没想到,这个早期的 KPI 项目能一路狂奔,演变成如今支撑企业级大型项目开发的核心语言。2015 年发布的 ES6(也就是 ECMAScript 2015)绝对是 JS 发展史上的分水岭。在 ES6 之前我们习惯叫 ES5,而在 ES6 之后,JS 进入了每年一更的 ES6+ 时代。
今天这篇笔记,我们不聊那些花里胡哨的新 API,就静下心来复习一下 ES6 最基础、也最容易踩坑的部分:变量声明与作用域。
作用域:变量的“户口”与生命周期
要理解 let 和 const,得先搞懂作用域(Scope)。你可以把作用域理解为变量的“户口所在地”。
在 JS 中,作用域是分层的:全局作用域在最外层,往下是函数局部作用域,再往下还有块级作用域 {}。当代码执行时,引擎查找变量有一套“冒泡规则”:先在当前作用域找,找不到就往外层找,如果一直找到全局作用域都没有,那就直接报错 ReferenceError: XXX is not defined。
从内存的角度看,变量的生命周期和作用域息息相关。代码运行进入某个函数或代码块时,会在内存中申请一块区域;当函数执行完毕退出后,这块内存就会被垃圾回收机制销毁。
告别 var:为什么我们需要块级作用域?
早期的 JS 只有全局作用域和函数作用域,没有块级作用域。这就导致了一个很尴尬的问题:用 var 声明的变量,会无视 if 或 for 的代码块限制,直接泄漏到外部。
javascript
编辑
var age = 100;
if (age > 12) {
var dog = age * 7; // dog 本意只在 if 里生效,但它跑到了全局
}
console.log(dog); // 居然能打印出来!
为了解决这种混乱,ES6 引入了 let 和 const,它们都支持块级作用域。一旦用这两个关键字在 {} 内声明变量,出了这个括号,变量就会自动被销毁。
let vs const:该用哪个?
- let:用来声明普通的变量。它的值可以改变,甚至在极端情况下类型都能变(虽然不推荐这么做,比如把数字改成字符串,极其不利于维护)。
- const:constant 的缩写,用来声明常量。它就像孙悟空的金箍棒,一旦绑定,就不允许重新赋值。
️ const 的一个经典误区
很多人以为 const 声明的值绝对不可变,这只对简单数据类型成立。对于复杂数据类型(如对象、数组),const 保证的是内存地址不变,而不是里面的内容不能改。
javascript
编辑
const person = { name: '张三', age: 18 };
person.age++; // 完全合法!修改属性不会报错
// person = {}; // TypeError: Assignment to constant variable. 这才是被禁止的
经典面试题:for + setTimeout 的坑
这是面试必考题,也是理解 var 和 let 区别的最佳案例:
javascript
编辑
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(`this number is ${i}`);
});
}
如果你用 var,控制台会连续输出 10 次 this number is 10。因为 var 没有块级作用域,整个循环共用一个全局的 i。等异步的 setTimeout 真正执行时,同步的 for 循环早就跑完了,i 已经变成了 10。
解法很简单,把 var 换成 let。
let 会为每一次循环创建一个独立的块级作用域,相当于嵌套了 n 个局部作用域,每次异步回调捕获的都是当时那个独立的 i,于是完美输出 0-9。
变量提升(Hoisting):反直觉的编译机制
JS 代码在执行前,会经历一个“编译阶段”,主要任务是准备执行上下文。在这个阶段,var 声明的变量会被提升到当前作用域的顶部,但只提升声明,不提升赋值。
javascript
编辑
console.log(pizza); // undefined (而不是报错)
var pizza = 'Deep Dish';
这种机制非常反直觉,很容易引发 bug。而 let 和 const 虽然也有提升,但在声明语句执行前,它们处于“暂时性死区”(Temporal Dead Zone),提前访问会直接抛出 ReferenceError: Cannot access 'pizza' before initialization。
所以,养成好习惯:尽量用 let 和 const 代替 var,优先使用 const,需要重新赋值时才用 let。 这不仅能避免变量提升带来的诡异问题,还能让代码的意图更加清晰。