一、JS 的传奇进化史
JavaScript 诞生于 1995 年,最初仅用于简单的页面交互,被戏称为 "网页小玩具"。直到 ES6(2015 年)的出现,它才真正蜕变为企业级开发语言 —— 就像从小作坊升级成了现代化工厂,支持块级作用域、常量声明等特性,彻底告别了 "老破小" 时代。
二、变量声明:从 var 到 let/const 的逆袭
1. var 的 "坑点" 大赏
- 没有块级作用域:在 for 循环里用
var i
,循环外也能访问i
,容易引发变量混乱。 - 变量提升:先使用后声明,代码逻辑容易看懵(比如
console.log(num); var num = 10;
会输出undefined
)。 - 污染全局:用 var 声明的变量会挂在 window 上,就像往公共泳池里乱倒颜料🌚。
2. let 的 "真香" 特性
-
块级作用域护体:
{ let a = 1; } console.log(a); // 报错:a is not defined
变量仅在花括号内有效,外部访问直接报错,作用域更清晰。
-
拒绝重复声明:同一作用域内
let a = 1; let a = 2;
会直接报错,避免手滑踩坑。
3. const 的 "倔强" 原则
-
声明时必须初始化:
const PI;
会报错,正确姿势是const PI = 3.14
。 -
简单类型值不变:
const num = 5; num = 6;
报错,但对象例外:const obj = { a: 1 }; obj.a = 2; // 允许(const 限制的是变量指向的内存地址,而非对象属性)
三、内存世界:栈与堆的分工哲学
1. 栈内存:小而快的 "子弹夹"
-
存储类型:Number、String 等简单类型,存取速度像坐火箭。
-
赋值规则:值传递,例如:
let a = 1; let b = a; b = 2; // a 仍为 1(b 复制了 a 的值,两者独立)
2. 堆内存:大而灵活的 "仓库"
-
存储类型:Object、Array 等复杂类型,栈内存存储其地址。
-
赋值规则:引用传递,例如:
let arr1 = [1]; let arr2 = arr1; arr2.push(2); // arr1 变为 [1, 2](两者指向同一块堆内存)
四、块级作用域:大型项目的 "秩序守护者"
在 for
/if
等花括号内,let/const 声明的变量不会 "乱跑"。例如:
for (let i = 0; i < 5; i++) {
// i 只在循环内有效
}
console.log(i); // 报错:i is not defined
这在多人协作时超重要,避免变量名冲突像打地鼠一样麻烦😅。
五、TDZ:干掉变量提升的 "正义使者"
TDZ(暂时性死区)是 let
/const
声明前的代码区域,访问变量会报错:
console.log(msg); // 报错:Cannot access 'msg' before initialization
let msg = 'hello'; // 声明前属于 TDZ
TDZ 让代码执行顺序与阅读顺序一致,再也不用 "反向推理" 变量声明啦~
六、实战场景:避坑指南
1. 循环定时器的坑
老代码(var 版) :
for (var i = 0; i < 10; i++) {
setTimeout(() => console.log(i), 1000); // 输出 10 个 10
}
原因:var 的 i 是全局变量,循环结束后 i = 10,所有定时器共享同一值。
正确姿势(let 版) :
for (let i = 0; i < 10; i++) {
setTimeout(() => console.log(i), 1000); // 依次输出 0-9
}
let 的 i 在每次循环中独立作用域,完美~
2. 数组赋值的 "陷阱"
const friends = [
{ "name": "小吴", "hometown": "上饶" },
{ "name": "小谢", "hometown": "赣州" }
];
const newFriends = friends; // 引用传递!
newFriends.push({ "name": "小邓" });
console.log(friends); // friends 也会包含小邓
解决方案:使用展开运算符浅拷贝(仅复制一层):
const newFriends = [...friends]; // 新数组指向不同堆内存
⚠️ 注意:浅拷贝无法复制嵌套对象,例如:
const friends = [
{ "name": "小吴", "info": { "age": 18 } }
];
const newFriends = [...friends];
newFriends[0].info.age = 20;
console.log(friends[0].info.age); // 输出 20(引用传递导致共享修改)
若需完全独立,需用深拷贝(如 JSON.parse(JSON.stringify(obj))
)。
七、总结:基础不牢,地动山摇
掌握变量声明、作用域、内存模型这些核心基础,就像给 JS 大厦打下了钢筋混凝土地基。接下来无论是学框架还是啃源码,都会轻松很多!快去用代码实战吧💪