基础语法与数据类型
约 1683 字大约 6 分钟
2025-11-24
变量声明
问题
变量声明的方式与它们的区别?
在 JavaScript 中,有多种变量声明方式,主要包括使用 var、let、const 关键字,下面详细介绍它们及其区别。
1. var
- 特点
- 函数作用域:
var声明的变量为函数作用域,意味着在函数内部任何位置声明的var变量,在整个函数内部都是可见的。 - 变量提升:使用
var声明的变量会被提升到当前作用域的顶部,可以在声明之前访问,但值为undefined。 - 可重复声明:在同一作用域内可以多次使用
var声明同一个变量。
- 函数作用域:
- 示例代码
function testVar() {
console.log(num); // 输出: undefined
var num = 10;
console.log(num); // 输出: 10
var num = 20; // 可以重复声明
console.log(num); // 输出: 20
}
testVar();想一想,下面的代码输出什么?为什么?
JS 代码示例
控制台输出
点击运行按钮执行代码...
2. let
- 特点
- 块级作用域:
let声明的变量具有块级作用域,变量只在声明它的块(如if语句、for循环、while循环等)内部可见。 - 不存在变量提升:
let声明的变量不会被提升到当前作用域的顶部,在声明之前访问会导致ReferenceError。 - 不可重复声明:在同一作用域内不能使用
let重复声明同一个变量。
- 块级作用域:
- 示例代码
function testLet() {
// console.log(count); // 报错: ReferenceError: Cannot access 'count' before initialization
let count = 10;
console.log(count); // 输出: 10
// let count = 20; // 报错: SyntaxError: Identifier 'count' has already been declared
if (true) {
let innerCount = 20;
console.log(innerCount); // 输出: 20
}
// console.log(innerCount); // 报错: ReferenceError: innerCount is not defined
}
testLet();3. const
- 特点
- 块级作用域:和
let一样,const声明的常量具有块级作用域。 - 不存在变量提升:
const声明的常量不会被提升到当前作用域的顶部,在声明之前访问会导致ReferenceError。 - 必须初始化:使用
const声明常量时,必须同时进行初始化,否则会报错。 - 常量值不可变(基本类型):一旦声明并初始化,基本类型的常量的值就不能再被重新赋值,但如果常量是引用类型(如对象、数组),可以修改其内部属性。
- 不可重复声明:在同一作用域内不能使用
const重复声明同一个常量。
- 块级作用域:和
- 示例代码
function testConst() {
// const PI; // 报错: SyntaxError: Missing initializer in const declaration
const PI = 3.14;
// PI = 3.14159; // 报错: TypeError: Assignment to constant variable.
console.log(PI); // 输出: 3.14
const person = { name: "Alice" };
person.name = "Bob"; // 可以修改对象的属性
console.log(person.name); // 输出: Bob
}
testConst();总结
| 声明方式 | 作用域 | 提升情况 | 可重复声明 | 初始化要求 | 值可变性 |
|---|---|---|---|---|---|
var | 函数作用域 | 变量提升 | 是 | 否 | 是 |
let | 块级作用域 | 无提升 | 否 | 否 | 是 |
const | 块级作用域 | 无提升 | 否 | 是 | 基本类型不可变,引用类型可修改内部属性 |
在实际开发中,建议优先使用 const 声明常量,当需要重新赋值时使用 let,尽量避免使用 var 以减少潜在的问题。
问题补充
想一想,下面的代码输出什么?
JS 代码示例
控制台输出
点击运行按钮执行代码...
for (var i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0);
}
for (let i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0);
}var声明的变量具有函数作用域,且在循环中共享同一个闭包变量i。当setTimeout回调执行时,循环早已结束,i的值为5。let声明的变量具有块级作用域,每次循环迭代都会创建一个新的词法环境,每个setTimeout回调捕获的是各自迭代中的i值。
for-in 与 for-of
问题
for...in 与 for...of 的区别?
for...in 与 for...of 是 JavaScript 中两种不同的遍历语法,用途、遍历目标和行为完全不同。
1. for...in
- 遍历对象的可枚举属性名(key/索引),包括继承的可枚举属性。
- 主要用于 对象(Object),但也可用于数组(不推荐)。
const obj = { a: 1, b: 2 };
for (const key in obj) {
console.log(key, obj[key]); // 'a' 1, 'b' 2
}
const arr = ["x", "y"];
for (const index in arr) {
console.log(index, arr[index]); // '0' 'x', '1' 'y' (输出字符串索引!)
}问题:
- 遍历数组时,
index是 字符串(如'0'),不是数字。
for...of
- 遍历可迭代对象的值(value),如数组、字符串、Map、Set、NodeList 等。
const arr = ["x", "y"];
for (const value of arr) {
console.log(value); // 'x', 'y'
}
const str = "hi";
for (const char of str) {
console.log(char); // 'h', 'i'
}
const map = new Map([
[1, "a"],
[2, "b"],
]);
for (const [k, v] of map) {
console.log(k, v); // 1 'a', 2 'b'
}优点:
- 直接拿到值,无需通过索引访问。
- 支持
break、continue、return。 - 顺序可靠(按迭代器协议定义的顺序)。
数据解构
解构的本质是按结构取值,是从原对象/数组中读取值赋值给新变量。解构时若读取到的是原始值,则赋值的是值的副本;但若读取到的是引用值时,那么赋值的是值的引用。
解构对象
对象根据 属性名 进行解构。
直接解构
const obj = { name: "Alice", age: 25 };
// 解构:变量名必须与属性名一致
const { name, age } = obj;
console.log(name); // 'Alice'
console.log(age); // 25重命名变量
如果想用不同名字接收属性值:
const { name: userName, age: userAge } = obj;
console.log(userName); // 'Alice'设置默认值
当属性不存在时提供默认值,存在则保持原值。
const { age = 18, gender = "unknown" } = obj;
console.log(gender); // 'unknown',因为 obj 没有 gender,使用默认值
console.log(age); // 25,因为 obj 有 age,使用原值解构嵌套对象
const user = {
id: 1,
profile: {
email: "[email protected]",
settings: { theme: "dark" },
},
};
const {
profile: { email },
profile: {
settings: { theme },
},
} = user;
// 或更简洁:
const {
profile: {
email,
settings: { theme },
},
} = user;
console.log(email); // '[email protected]'
console.log(theme); // 'dark'剩余属性
const { name, ...rest } = { name: "Bob", age: 30, city: "Beijing" };
console.log(name); // 'Bob'
console.log(rest); // { age: 30, city: 'Beijing' }在函数参数中解构
function greet({ name, age }) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
greet({ name: "Charlie", age: 40 }); // Hello, Charlie! You are 40 years old.解构数组
数组中按 位置(索引) 解构数据。
基础用法
const arr = [10, 20, 30];
const [a, b, c] = arr;
console.log(a, b, c); // 10 20 30跳过元素
const [first, , third] = arr; // 跳过第二个元素
console.log(first, third); // 10 30默认值
const [x, y = 100] = [5]; // y 未提供,使用默认值
console.log(x, y); // 5 100剩余元素
const [head, ...rest] = [1, 2, 3, 4];
console.log(head); // 1
console.log(rest); // [2, 3, 4]交换变量(无需临时变量)
let a = 1,
b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1