基础语法与数据类型
约 2429 字大约 8 分钟
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();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 以减少潜在的问题。
问题补充
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):
5
5
5
5
5第二块(let):
0
1
2
3
4var声明的变量具有函数作用域,且在循环中共享同一个闭包变量i。当setTimeout回调执行时,循环早已结束,i的值为5。let声明的变量具有块级作用域,每次循环迭代都会创建一个新的词法环境,每个setTimeout回调捕获的是各自迭代中的i值。
数据类型
问题
变量声明的方式与它们的区别?
在 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();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 以减少潜在的问题。
问题补充
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):
5
5
5
5
5第二块(let):
0
1
2
3
4var声明的变量具有函数作用域,且在循环中共享同一个闭包变量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 { name, gender = 'unknown' } = obj;
console.log(gender); // 'unknown'(因为 obj 没有 gender)解构嵌套对象
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