📚 JavaScript 变量声明三剑客:`var`、`let`、`const` 学习笔记


🌟 前言:你不知道的 JavaScript 小知识,你搞明白了吗?

"JavaScript 是一门灵活的语言,但也正因为它的'宽容',埋下了许多陷阱。"

------《你不知道的 JavaScript》

你是否遇到过:

  • ❓ 为什么变量还没定义就能访问,值却是 undefined?
  • ❓ 为什么 const 定义的对象,属性还能改?
  • ❓ 为什么 let 变量不能提前访问,会报 Cannot access before initialization?

这些问题的背后,正是 JavaScript 变量声明机制的"暗流"------变量提升 、暂时性死区 、作用域规则。

今天,我们就来揭开 var、let、const 的神秘面纱,带你彻底搞懂 JavaScript 的变量声明机制。


🧩 一、JavaScript 中如何声明变量?

JavaScript 提供了三种声明变量的方式:

关键字 用途 特性
var 声明变量(ES5 及以前) ❌ 变量提升、函数作用域、可重复声明
let 声明块级变量(ES6) ✅ 块级作用域、无变量提升(有暂时性死区)、不可重复声明
const 声明常量(ES6) ✅ 块级作用域、必须初始化、不可重新赋值

💡 推荐 :
ES6 之后,建议不再使用 var ,优先使用 let 和 const。


📦 二、var:被时代淘汰的"糟粕"

1. 变量提升(Hoisting)

var 声明的变量会在编译阶段被提升到作用域顶部。

ini 复制代码
javascript
编辑
console.log(age); // 输出: undefined
var age = 18;

执行过程解析:

javascript 复制代码
javascript
编辑
// 实际执行顺序(编译阶段已提升)
var age;           // 变量提升,值为 undefined
console.log(age);  // undefined
age = 18;          // 赋值

⚠️ 问题 :

这种"先使用后声明"的行为不符合直觉,容易导致 bug。


2. 函数提升(Function Hoisting)

函数声明也会被提升,且函数体也会被提升。

scss 复制代码
javascript
编辑
setWidth(); // 正常执行

function setWidth() {
    var width = 100;
    console.log(width); // 输出: 100
}

对比:函数表达式不会完全提升

scss 复制代码
javascript
编辑
setHeight(); // TypeError: setHeight is not a function

var setHeight = function() {
    console.log(200);
};

📌 核心区别:

  • function fn() {}:函数声明 → 声明 + 赋值 都提升
  • var fn = function() {}:函数表达式 → 只有声明提升

🔒 三、let 和 const:ES6 的救星

1. 块级作用域(Block Scope)

let 和 const 只在 {} 块内有效。

ini 复制代码
javascript
编辑
{
    let a = 1;
    const b = 2;
}
console.log(a); // ReferenceError: a is not defined

✅ 优势:避免变量污染全局作用域。


2. 暂时性死区(Temporal Dead Zone, TDZ)

在 let/const 声明之前访问变量,会报错。

ini 复制代码
javascript
编辑
console.log(PI); // ReferenceError: Cannot access 'PI' before initialization
const PI = 3.1415926;

💡 为什么设计 TDZ?

防止开发者误用"提升"特性,提高代码可读性和安全性。


3. const 的"常量"真相

const 并不意味着"值不可变",而是引用不可变。

✅ 简单类型:不可修改

ini 复制代码
javascript
编辑
const key = 'abc123';
key = 'abc234'; // TypeError: Assignment to constant variable.

✅ 复杂类型:可修改属性

ini 复制代码
javascript
编辑
const person = {
    name: "ysw",
    age: 28,
};

person.age = 21; // ✅ 允许,修改的是对象内部属性
console.log(person); // { name: "ysw", age: 21 }

person = {}; // ❌ 报错,不能重新赋值

🔒 如何真正"冻结"对象?

ini 复制代码
javascript
编辑
const wes = Object.freeze(person);
wes.age = 17; // ❌ 在严格模式下报错,非严格模式静默失败
console.log(wes); // age 仍为 21

📌 Object.freeze() :浅冻结,只冻结对象自身属性,不递归冻结嵌套对象。


🧠 四、三大错误类型解析

错误 原因 示例
ReferenceError: height is not defined 变量未声明,作用域外调用 console.log(height);(未声明)
TypeError: Assignment to constant variable. 尝试修改 const 变量 const a = 1; a = 2;
ReferenceError: Cannot access 'PI' before initialization 访问了暂时性死区中的变量 console.log(PI); const PI = 3.14;

🖼️ 五、图解:变量声明机制

📌 关键记忆:

  • var:提升 → 赋值 → 访问
  • let/const:提升 → TDZ → 赋值 → 访问

💼 六、大厂高频面试题(变量声明专项)

❓ Q1: var、let、const 有什么区别?

✅ 参考回答:

特性 var let const
作用域 函数作用域 块级作用域 块级作用域
变量提升 ✅ ❌(有 TDZ) ❌(有 TDZ)
重复声明 ✅ ❌ ❌
重新赋值 ✅ ✅ ❌

📌 总结:var 是历史遗留,let 用于变量,const 用于常量。


❓ Q2: 什么是暂时性死区(TDZ)?为什么设计它?

✅ 参考回答 :

暂时性死区是指 let/const 变量从作用域开始到声明语句之间的区域。在此区域内访问变量会报错。

设计目的:

  1. 避免 var 提升带来的"先使用后声明"陷阱
  2. 提高代码可读性和安全性
  3. 强制开发者遵循"先声明后使用"的良好习惯

❓ Q3: const 定义的对象属性能改吗?如何真正冻结?

✅ 参考回答 :

可以。const 只保证引用地址不变,不保证对象内部属性不变。

使用 Object.freeze(obj) 可以冻结对象,使其属性不可修改。但它是浅冻结,嵌套对象仍可修改。

ini 复制代码
javascript
编辑
const obj = { a: { b: 1 } };
Object.freeze(obj);
obj.a.b = 2; // ✅ 仍可修改

💡 加分项:可用 deepFreeze 实现深冻结。


❓ Q4: function 和 var 的提升有什么区别?

✅ 参考回答:

  • var:只提升声明,不提升赋值
  • function:提升声明和函数体(函数声明)
  • 函数表达式:行为类似 var,只提升声明
scss 复制代码
javascript
编辑
foo(); // TypeError
var foo = function() {};

bar(); // 正常执行
function bar() {}

❓ Q5: 为什么建议不再使用 var?

✅ 参考回答:

  1. 变量提升导致"先使用后声明",易出 bug
  2. 函数作用域容易污染全局变量
  3. 可重复声明增加维护成本
  4. let/const 提供了更安全、更清晰的块级作用域

📌 现代开发应优先使用 let 和 const。


✅ 七、总结:变量声明最佳实践

场景 推荐 说明
普通变量 let 块级作用域,安全
常量 const 防止意外修改
对象/数组 const + Object.freeze()(如需完全冻结) 保证引用和内容不变
循环变量 let 避免闭包问题
全局变量 尽量避免 使用模块化或立即执行函数

🌟 记住口诀 :
"能用 const 不用 let,能用 let 不用 var"
"先声明,后使用,TDZ 拒绝提前访问"


🚀 下一步

  • ✅ 实践:用 let/const 重构旧项目中的 var
  • ✅ 演练:故意触发 TDZ 错误,理解其机制
  • ✅ 进阶:学习 Object.freeze() 和深冻结实现

💡 JavaScript 的"糟粕"已被淘汰,拥抱 ES6+ 的"精华" ,写出更安全、更可维护的代码! ✨

相关推荐
半个落月11 小时前
LangSmith 全链路观测(下):用 Dataset 与 LLM Judge 量化评估 RA
javascript·langchain
半个落月11 小时前
LangSmith 全链路观测(上):用 LangGraph 与 Milvus 构建可追踪的 RAG 客服
javascript·langchain
半个落月12 小时前
LangChain.js Agent 中间件实战:生命周期、动态工具与文件权限
javascript·langchain
志尊宝13 小时前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js
bmxy小明同学19 小时前
2026-10-11-幻觉治理
服务器·前端·javascript
m0_5873830019 小时前
无人自助拼豆系统落地开发实战指南 核心逻辑实现与部署经验分享
前端·javascript·vue.js·人工智能·经验分享·需求分析
zhaiyy19 小时前
HTML Popover API 实践
前端·javascript·html
OpsEye20 小时前
企业 AI 管理新需求:不仅要接入模型,还要看懂每一次调用
javascript·ai编程
牧艺20 小时前
SPA 也能像原生 App:View Transitions API 从原理到可上线实战
前端·javascript·css
Dovis(誓平步青云)20 小时前
现场问题先关闭还是先验收,建筑任务怎样处理依赖
前端·javascript·数据库·mysql·音视频