JavaScript 蹭了 Java 的热度,是一种弱类型动态语言,早期设计用来给网页添加交互(幻灯片)和 DOM 编程。
JS 以 ECMAScript(E 为欧洲)为语言标准,ES6 是 JS 的新版本(2015 年发布),让 JS 能够用于企业级大型项目开发。
(注:JS 早期是一个 KPI 项目,有些设计瑕疵。)
1. 声明变量并赋值
var(ES5,不推荐使用)let(ES6+,替代var)const(ES6+,声明常量)
2. 作用域(Scope)
2.1 作用域嵌套
- 全局作用域
- 函数局部作用域
- 块级作用域
{ ... }
变量属于某个作用域,JS 是弱类型,变量的类型由值决定。
2.2 查找变量的规则(冒泡查找)
- 先在当前作用域查找,找到了就使用。
- 如果没有找到,向外层作用域查找(冒泡)。
- 直到全局作用域都没找到,就报错:
ReferenceError: xxx is not defined。
2.3 变量的生命周期
函数或代码块执行完毕后,会被垃圾回收,释放内存。变量的声明会在内存中申请区域,销毁时回收。
3. var、let、const 的区别
3.1 var 的问题
- 不支持块级作用域。
- 没有常量概念,只能靠代码规范约束(例如
var PI = 3.14)。 - 存在变量提升(hoisting),可能违反直觉。
3.2 let 和 const(支持块级作用域)
let:声明变量,值可以改变,类型也可以改变(但不要这么干)。const:声明常量,声明时必须赋值,简单数据类型的值不可改变;复杂数据类型(如对象)的值可以改变,但变量指向的内存地址不能改变。
javascript
const item = 1;
let a; // undefined
// 简单数据类型
const key = 'abc123';
key = 'ABC123'; // ❌ Assignment to constant variable
let points = 50;
points = 51;
points = "52"; // 允许但不推荐
let winner = false;
winner = '戴';
// 复杂数据类型:对象
const person = {
name: '张三',
age: 18
};
person.age++; // ✅ 允许修改对象属性
person = "111"; // ❌ 不能重新赋值
4. for 循环中的 var 与 let + setTimeout
4.1 使用 var(不支持块级作用域)
循环变量 i 只有一个(函数作用域或全局),循环结束后 i 变成 10。
所有 setTimeout 回调都引用同一个 i,因此打印的都是 10。
javascript
for (var i = 0; i < 10; i++) {
console.log(i); // 立即输出 0..9
setTimeout(function() {
console.log(`The number is ${i}`); // 一秒后全部输出 10
}, 1000);
}
4.2 使用 let(支持块级作用域)
每次迭代都会创建一个独立的块级作用域,每个 i 都被单独保存。
setTimeout 回调引用各自作用域中的 i,因此打印 0..9。
javascript
for (let i = 0; i < 10; i++) {
console.log(i);
setTimeout(function() {
console.log(`The number is ${i}`); // 一秒后依次输出 0..9
}, 1000);
}
5. 变量提升(Hoisting)
- 代码执行分为两个阶段:编译阶段 和 执行阶段。
- 编译阶段会准备执行上下文(全局执行上下文),此时用
var声明的变量会被提升,初始值为undefined。 - 执行阶段才真正赋值。
javascript
console.log(pizza); // ❌ ReferenceError: Cannot access 'pizza' before initialization
let pizza = 'Deep Dish';
注意:let 和 const 不支持变量提升,在声明前访问会报错(暂时性死区)。
变量提升是一种容易出错的设计,应避免使用 var,始终使用 let 和 const。
6. 代码示例综合
javascript
var height = 200; // 全局作用域
function setwidth() {
var width = 100; // 局部作用域
console.log(width, height);
}
setwidth();
var age = 100;
if (age > 12) {
// 块级作用域
var dog = age * 7; // var 不受块级限制,会泄漏到外部
let x = 111; // let 只在块内有效
console.log(dog);
dog++;
}
console.log(dog); // 可以访问,输出 701
// console.log(x); // ❌ x is not defined
// 全局块级作用域
{
const name = '张三';
console.log(name);
}
// console.log(name); // ❌ name is not defined
总结
| 特性 | var | let | const |
|---|---|---|---|
| 作用域 | 函数作用域 | 块级 | 块级 |
| 变量提升 | 有 | 无 | 无 |
| 重复声明 | 允许 | 不允许 | 不允许 |
| 必须初始化 | 否 | 否 | 是 |
| 值能否改变 | 能 | 能 | 简单类型不能,复杂类型的属性可以 |
最佳实践:
- 禁止使用
var。 - 默认使用
const,只有当变量确实需要重新赋值时才使用let。