JS 入门通关手册(36):变量提升、暂时性死区与块级作用域

摘要

本文系统讲解 JavaScript 变量提升机制、暂时性死区(TDZ)以及 ES6 块级作用域,通过大量可运行案例清晰展示 var/let/const 在声明、提升、作用域上的差异,总结日常开发中的常见陷阱与最佳实践,帮助开发者从底层理解变量行为,避免隐性 bug,夯实前端基础。


一、变量提升:JavaScript 先 "扫描" 再执行

在 JS 执行代码前,引擎会先遍历当前作用域,把变量声明和函数声明 提升到作用域顶部,这一机制称为变量提升(Hoisting)

关键点:

  • 只提升声明,不提升赋值
  • 函数声明整体提升,可在定义前调用
  • var 变量提升为 undefined

1. var 变量提升

javascript

运行

javascript 复制代码
console.log(name); // undefined
var name = "前端开发";

// 引擎实际执行顺序
var name;
console.log(name);
name = "前端开发";

2. 函数声明提升

javascript

运行

javascript 复制代码
foo(); // 正常执行:hello

function foo() {
  console.log("hello");
}

3. 函数表达式不会提升

javascript

运行

javascript 复制代码
foo(); // TypeError: foo is not a function

var foo = function () {
  console.log("hello");
};

4. 函数提升优先级高于变量

同名情况下,函数声明会覆盖 var 声明

javascript

运行

javascript 复制代码
console.log(foo); // [Function: foo]

var foo = 123;
function foo() {}

二、let /const 也会提升,但存在暂时性死区

很多人误以为 let/const 不提升,实际上它们同样提升 ,只是进入暂时性死区(TDZ),无法提前访问。

javascript

运行

javascript 复制代码
console.log(age); 
// Uncaught ReferenceError: Cannot access 'age' before initialization

let age = 20;

什么是暂时性死区 TDZ?

从作用域开始,到变量声明语句之间的区域,就是该变量的暂时性死区。在死区内访问变量会直接报错。

javascript

运行

javascript 复制代码
{
  // 此处开始到 let num 之前都是 TDZ
  console.log(num); // 报错
  let num = 10;
  // TDZ 结束
}

经典陷阱:

javascript

运行

javascript 复制代码
let a = 1;
{
  console.log(a); // 报错!受内部 let 影响进入 TDZ
  let a = 2;
}

三、ES6 块级作用域:{} 就是一个作用域

ES6 之前只有全局作用域函数作用域 。ES6 新增块级作用域 ,由 {} 包裹,if / for / while / try 都会形成块级作用域。

let /const 具有块级作用域

javascript

运行

javascript 复制代码
{
  let name = "张三";
  const age = 20;
}
console.log(name); // ReferenceError

var 不受块级作用域限制

javascript

运行

javascript 复制代码
{
  var num = 100;
}
console.log(num); // 100,正常访问

典型场景:循环事件绑定问题

javascript

运行

javascript 复制代码
// var 会泄露,输出都是 5
for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 0);
}

// let 具有块级作用域,输出 0 1 2 3 4
for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 0);
}

四、var /let/const 全方位对比

表格

特性 var let const
作用域 函数 / 全局 块级 块级
变量提升 是,值为 undefined 是,但 TDZ 不可访问 是,但 TDZ 不可访问
重复声明 允许 不允许 不允许
重新赋值 支持 支持 不支持
初始赋值 非必须 非必须 必须
挂载到 window
开发推荐 不推荐 可变值使用 优先使用

五、常见面试题

  1. **变量提升是什么?为什么会出现?**答:JS 执行前会扫描作用域并提升声明,只提升声明不提升赋值,目的是为了让函数可以在定义前调用。

  2. **let 存在变量提升吗?**答:存在提升,但受暂时性死区限制,声明前访问会抛出引用错误。

  3. **const 定义的对象可以修改属性吗?**答:可以修改属性与成员,不能修改引用(重新赋值)。

  4. **暂时性死区的意义是什么?**答:强制规范变量先声明后使用,减少隐性错误,提升代码健壮性。

  5. **为什么现代项目推荐 const > let > var?**答:const 语义明确、不可变、更安全;let 避免变量污染;var 易产生提升与全局污染问题。


六、总结

  1. 变量提升是 JS 执行前的声明扫描机制,只提升声明不提升赋值。
  2. 函数声明提升优先级高于 var 变量声明。
  3. let/const 存在提升,但受暂时性死区 TDZ 保护。
  4. let/const 支持块级作用域,可有效避免变量污染与循环问题。
  5. 现代开发规范:优先使用 const,需要变化时使用 let,尽量不使用 var。
相关推荐
拾年27512 分钟前
Bun:重新定义 JavaScript 运行时 - 为什么它可能是 Node.js 的终结者?
javascript·typescript·bun
数据知道19 分钟前
网站到底是如何通过JS读取你的浏览器指纹的?
开发语言·javascript·ecmascript·指纹浏览器
用户9385156350723 分钟前
从JS的“坑”到TS的“墙”,再到Bun与AI:打造健壮的全栈应用
前端·javascript
橘子星25 分钟前
浅谈 TypeScript 与 Bun:现代 JavaScript 开发的利器
前端·javascript
铁皮饭盒29 分钟前
Bun 的三种并发"暗器":reusePort、Worker、spawn,能硬刚 Java 吗?
前端·javascript·后端
ldmd2841 小时前
Typescript 入门篇-3
javascript·typescript·notepad++
Cobyte1 小时前
18.【SolidJS】 采用 template 内容模板元素创建 DOM 元素
前端·javascript·vue.js
怕浪猫1 小时前
Electron 开发实战(十二):安全性最佳实践|彻底杜绝漏洞、代码执行与数据泄露
前端·javascript·electron
weixin_446260852 小时前
Typora 插件开发实战:基于 JavaScript/HTML 构建定制化 Markdown 扩展
开发语言·javascript·html
如烟花的信页2 小时前
某管理服务平台点选逆向分析
javascript·爬虫·python·js逆向