控制结构(Control Flow)
JavaScript 中的控制结构:条件语句、循环语句的使用和底层原理
📖 入门:基础使用
1. 条件语句
1.1 if 语句
基本语法:
if (condition) {
// 代码块
}示例:
let x = 10;
if (x > 5) {
console.log("x is greater than 5");
}1.2 if...else 语句
基本语法:
if (condition) {
// 代码块 1
} else {
// 代码块 2
}示例:
let age = 18;
if (age >= 18) {
console.log("Adult");
} else {
console.log("Minor");
}1.3 if...else if...else 语句
基本语法:
if (condition1) {
// 代码块 1
} else if (condition2) {
// 代码块 2
} else {
// 代码块 3
}示例:
let score = 85;
if (score >= 90) {
console.log("A");
} else if (score >= 80) {
console.log("B");
} else if (score >= 70) {
console.log("C");
} else {
console.log("D");
}1.4 三元运算符
基本语法:
condition ? valueIfTrue : valueIfFalse示例:
let age = 20;
let status = age >= 18 ? "Adult" : "Minor";
console.log(status); // "Adult"2. switch 语句
基本语法:
switch (expression) {
case value1:
// 代码块 1
break;
case value2:
// 代码块 2
break;
default:
// 默认代码块
}示例:
let fruit = "apple";
switch (fruit) {
case "banana":
console.log("Yellow fruit");
break;
case "apple":
console.log("Red or green fruit");
break;
case "orange":
console.log("Orange fruit");
break;
default:
console.log("Unknown fruit");
}注意:break 语句防止”穿透”(fall-through)
3. 循环语句
3.1 for 循环
基本语法:
for (initialization; condition; increment) {
// 代码块
}示例:
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}3.2 for...in 循环
基本语法:
for (key in object) {
// 代码块
}示例:
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
console.log(key, obj[key]); // a 1, b 2, c 3
}3.3 for...of 循环
基本语法:
for (value of iterable) {
// 代码块
}示例:
const arr = [1, 2, 3, 4, 5];
for (let value of arr) {
console.log(value); // 1, 2, 3, 4, 5
}3.4 while 循环
基本语法:
while (condition) {
// 代码块
}示例:
let i = 0;
while (i < 5) {
console.log(i); // 0, 1, 2, 3, 4
i++;
}3.5 do...while 循环
基本语法:
do {
// 代码块
} while (condition);示例:
let i = 0;
do {
console.log(i); // 0, 1, 2, 3, 4
i++;
} while (i < 5);特点:至少执行一次循环体
4. 循环控制语句
4.1 break 语句
作用:跳出循环
示例:
for (let i = 0; i < 10; i++) {
if (i === 5) {
break; // 跳出循环
}
console.log(i); // 0, 1, 2, 3, 4
}4.2 continue 语句
作用:跳过当前迭代,继续下一次循环
示例:
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) {
continue; // 跳过偶数
}
console.log(i); // 1, 3, 5, 7, 9
}🚀 提高:底层原理
1. 执行流程控制
1.1 条件语句的执行机制
if 语句的执行流程:
1. 计算条件表达式
2. 将结果转换为布尔值(Truthy/Falsy)
3. 根据布尔值决定执行路径
4. 创建新的执行上下文(如果需要)
示例分析:
if (x > 5) {
// 创建新的块级作用域
let y = 10;
console.log(y);
}
// y 在这里不可访问1.2 布尔值转换(Truthy/Falsy)
Falsy 值(会被转换为 false):
false0-00n(BigInt)""(空字符串)nullundefinedNaN
Truthy 值(会被转换为 true):
- 除了 Falsy 值之外的所有值
示例:
if (0) {
// 不会执行
}
if (1) {
// 会执行
}
if ("") {
// 不会执行
}
if ("hello") {
// 会执行
}2. switch 语句的底层实现
2.1 跳转表(Jump Table)
原理:
switch语句通常使用跳转表优化- 对于连续的值,使用跳转表比多个
if更高效 - 对于稀疏的值,使用二分查找或哈希表
性能对比:
// 多个 if(O(n))
if (x === 1) { /* ... */ }
else if (x === 2) { /* ... */ }
else if (x === 3) { /* ... */ }
// switch(可能优化为 O(1))
switch (x) {
case 1: /* ... */ break;
case 2: /* ... */ break;
case 3: /* ... */ break;
}2.2 严格相等比较
switch 使用严格相等(===):
let x = "1";
switch (x) {
case 1: // 不会匹配(类型不同)
console.log("number");
break;
case "1": // 会匹配
console.log("string");
break;
}3. 循环语句的执行机制
3.1 for 循环的执行流程
执行步骤:
1. 执行初始化表达式(initialization)
2. 计算条件表达式(condition)
3. 如果为 true:
a. 执行循环体
b. 执行增量表达式(increment)
c. 回到步骤 2
4. 如果为 false:退出循环
示例分析:
for (let i = 0; i < 3; i++) {
console.log(i);
}
// 执行过程:
// 1. let i = 0 (初始化)
// 2. i < 3? true (条件检查)
// 3. console.log(0) (执行循环体)
// 4. i++ (i = 1) (增量)
// 5. i < 3? true (条件检查)
// 6. console.log(1) (执行循环体)
// 7. i++ (i = 2) (增量)
// 8. i < 3? true (条件检查)
// 9. console.log(2) (执行循环体)
// 10. i++ (i = 3) (增量)
// 11. i < 3? false (条件检查)
// 12. 退出循环3.2 循环作用域和闭包
常见陷阱:
// 问题:所有 setTimeout 都打印 5
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // 5, 5, 5, 5, 5
}, 100);
}
// 解决方案 1:使用 let(块级作用域)
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // 0, 1, 2, 3, 4
}, 100);
}
// 解决方案 2:使用 IIFE
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(() => {
console.log(j); // 0, 1, 2, 3, 4
}, 100);
})(i);
}原理:
var属于函数作用域,循环结束后i为 5let属于块级作用域,每次循环创建新的绑定- IIFE 创建新的作用域,捕获当前的
i值
3.3 for...in vs for...of
for...in:
- 遍历对象的可枚举属性(包括继承的)
- 遍历顺序不确定(除了数组索引)
- 返回的是键名(key)
for...of:
- 遍历可迭代对象的值
- 遍历顺序确定
- 返回的是值(value)
示例对比:
const arr = [1, 2, 3];
arr.customProp = "custom";
// for...in:遍历所有可枚举属性
for (let key in arr) {
console.log(key); // "0", "1", "2", "customProp"
}
// for...of:遍历可迭代的值
for (let value of arr) {
console.log(value); // 1, 2, 3
}4. 循环优化
4.1 循环展开(Loop Unrolling)
原理:
- 减少循环次数
- 减少条件检查次数
示例:
// 优化前
for (let i = 0; i < 4; i++) {
process(arr[i]);
}
// 优化后(循环展开)
process(arr[0]);
process(arr[1]);
process(arr[2]);
process(arr[3]);4.2 循环不变式外提(Loop Invariant Code Motion)
原理:
- 将循环中不变的计算移到循环外
示例:
// 优化前
for (let i = 0; i < arr.length; i++) {
let result = expensiveFunction() * arr[i];
console.log(result);
}
// 优化后
const expensiveResult = expensiveFunction();
for (let i = 0; i < arr.length; i++) {
let result = expensiveResult * arr[i];
console.log(result);
}📝 最佳实践
- 使用
let代替var:避免作用域问题 - 优先使用
for...of:更简洁、更安全 - 避免在循环中创建函数:使用
let或 IIFE - 合理使用
break和continue:提高代码可读性 - 优化循环性能:减少不必要的计算