控制结构(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):

  • false
  • 0
  • -0
  • 0n (BigInt)
  • "" (空字符串)
  • null
  • undefined
  • NaN

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 为 5
  • let 属于块级作用域,每次循环创建新的绑定
  • 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);
}

📝 最佳实践

  1. 使用 let 代替 var:避免作用域问题
  2. 优先使用 for...of:更简洁、更安全
  3. 避免在循环中创建函数:使用 let 或 IIFE
  4. 合理使用 breakcontinue:提高代码可读性
  5. 优化循环性能:减少不必要的计算

🔗 相关链接


javascript 控制结构 循环 条件语句