JavaScript|第6章:流程控制语句

JavaScript|第6章:流程控制语句

流程控制语句

  • JavaScript|第6章:流程控制语句
    • 引言
    • [6.1 条件语句:if...else 与 switch](#6.1 条件语句:if...else 与 switch)
      • [6.1.1 if...else 语句](#6.1.1 if...else 语句)
      • [6.1.2 三元运算符:简洁的条件表达式](#6.1.2 三元运算符:简洁的条件表达式)
      • [6.1.3 switch 语句](#6.1.3 switch 语句)
      • [6.1.4 if...else vs switch:如何选择?](#6.1.4 if...else vs switch:如何选择?)
    • [6.2 循环语句:for、while、do...while](#6.2 循环语句:for、while、do...while)
      • [6.2.1 for 循环](#6.2.1 for 循环)
      • [6.2.2 while 循环](#6.2.2 while 循环)
      • [6.2.3 do...while 循环](#6.2.3 do...while 循环)
      • [6.2.4 三种循环对比](#6.2.4 三种循环对比)
      • [6.2.5 for...of 与 for...in 的区别](#6.2.5 for...of 与 for...in 的区别)
    • [6.3 break、continue 与标签语句](#6.3 break、continue 与标签语句)
      • [6.3.1 break:跳出循环](#6.3.1 break:跳出循环)
      • [6.3.2 continue:跳过本次迭代](#6.3.2 continue:跳过本次迭代)
      • [6.3.3 标签语句(Label Statement)](#6.3.3 标签语句(Label Statement))
    • [6.4 异常处理:try...catch...finally](#6.4 异常处理:try...catch...finally)
      • [6.4.1 基本语法](#6.4.1 基本语法)
      • [6.4.2 throw 语句](#6.4.2 throw 语句)
      • [6.4.3 Error 对象](#6.4.3 Error 对象)
      • [6.4.4 自定义 Error 类](#6.4.4 自定义 Error 类)
      • [6.4.5 finally 的执行时机](#6.4.5 finally 的执行时机)
      • [6.4.6 try...catch 的常见应用场景](#6.4.6 try...catch 的常见应用场景)
    • [6.5 实战:猜数字游戏](#6.5 实战:猜数字游戏)
      • [6.5.1 需求分析](#6.5.1 需求分析)
      • [6.5.2 命令行版本(Node.js)](#6.5.2 命令行版本(Node.js))
      • [6.5.3 代码结构解析](#6.5.3 代码结构解析)
      • [6.5.4 进阶版本:二分查找策略](#6.5.4 进阶版本:二分查找策略)
    • [6.6 常见面试题精讲](#6.6 常见面试题精讲)
      • [面试题 1:for...in 和 for...of 的区别是什么?](#面试题 1:for...in 和 for...of 的区别是什么?)
      • [面试题 2:以下代码输出什么?为什么?](#面试题 2:以下代码输出什么?为什么?)
      • [面试题 3:try...catch 能捕获所有错误吗?](#面试题 3:try...catch 能捕获所有错误吗?)
      • [面试题 4:switch 使用 == 还是 === 比较?](#面试题 4:switch 使用 == 还是 === 比较?)
    • 本章小结

上篇回顾第5章:运算符与表达式

下篇预告第7章:函数基础


引言

在前面的章节中,我们学习了变量、数据类型、类型转换和运算符。但到目前为止,我们的代码都是从上到下逐行执行的------这被称为"顺序结构"。

现实世界的问题远比顺序执行复杂。你需要根据用户的年龄判断是否成年,需要遍历一个数组找到所有偶数,需要在输入非法数据时优雅地处理错误......这些场景都需要流程控制
#mermaid-svg-MmCy9mtqGym2pLWY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MmCy9mtqGym2pLWY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MmCy9mtqGym2pLWY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MmCy9mtqGym2pLWY .error-icon{fill:#552222;}#mermaid-svg-MmCy9mtqGym2pLWY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MmCy9mtqGym2pLWY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MmCy9mtqGym2pLWY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MmCy9mtqGym2pLWY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MmCy9mtqGym2pLWY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MmCy9mtqGym2pLWY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MmCy9mtqGym2pLWY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MmCy9mtqGym2pLWY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MmCy9mtqGym2pLWY .marker.cross{stroke:#333333;}#mermaid-svg-MmCy9mtqGym2pLWY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MmCy9mtqGym2pLWY p{margin:0;}#mermaid-svg-MmCy9mtqGym2pLWY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MmCy9mtqGym2pLWY .cluster-label text{fill:#333;}#mermaid-svg-MmCy9mtqGym2pLWY .cluster-label span{color:#333;}#mermaid-svg-MmCy9mtqGym2pLWY .cluster-label span p{background-color:transparent;}#mermaid-svg-MmCy9mtqGym2pLWY .label text,#mermaid-svg-MmCy9mtqGym2pLWY span{fill:#333;color:#333;}#mermaid-svg-MmCy9mtqGym2pLWY .node rect,#mermaid-svg-MmCy9mtqGym2pLWY .node circle,#mermaid-svg-MmCy9mtqGym2pLWY .node ellipse,#mermaid-svg-MmCy9mtqGym2pLWY .node polygon,#mermaid-svg-MmCy9mtqGym2pLWY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MmCy9mtqGym2pLWY .rough-node .label text,#mermaid-svg-MmCy9mtqGym2pLWY .node .label text,#mermaid-svg-MmCy9mtqGym2pLWY .image-shape .label,#mermaid-svg-MmCy9mtqGym2pLWY .icon-shape .label{text-anchor:middle;}#mermaid-svg-MmCy9mtqGym2pLWY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MmCy9mtqGym2pLWY .rough-node .label,#mermaid-svg-MmCy9mtqGym2pLWY .node .label,#mermaid-svg-MmCy9mtqGym2pLWY .image-shape .label,#mermaid-svg-MmCy9mtqGym2pLWY .icon-shape .label{text-align:center;}#mermaid-svg-MmCy9mtqGym2pLWY .node.clickable{cursor:pointer;}#mermaid-svg-MmCy9mtqGym2pLWY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MmCy9mtqGym2pLWY .arrowheadPath{fill:#333333;}#mermaid-svg-MmCy9mtqGym2pLWY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MmCy9mtqGym2pLWY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MmCy9mtqGym2pLWY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MmCy9mtqGym2pLWY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MmCy9mtqGym2pLWY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MmCy9mtqGym2pLWY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MmCy9mtqGym2pLWY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MmCy9mtqGym2pLWY .cluster text{fill:#333;}#mermaid-svg-MmCy9mtqGym2pLWY .cluster span{color:#333;}#mermaid-svg-MmCy9mtqGym2pLWY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MmCy9mtqGym2pLWY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MmCy9mtqGym2pLWY rect.text{fill:none;stroke-width:0;}#mermaid-svg-MmCy9mtqGym2pLWY .icon-shape,#mermaid-svg-MmCy9mtqGym2pLWY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MmCy9mtqGym2pLWY .icon-shape p,#mermaid-svg-MmCy9mtqGym2pLWY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MmCy9mtqGym2pLWY .icon-shape .label rect,#mermaid-svg-MmCy9mtqGym2pLWY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MmCy9mtqGym2pLWY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MmCy9mtqGym2pLWY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MmCy9mtqGym2pLWY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 程序三大结构
顺序结构
分支结构
循环结构
if...else
switch
for / while
do...while

本章目标

  • 掌握 if...elseswitch 条件语句的用法与选择策略
  • 熟练使用 forwhiledo...while 三种循环
  • 理解 breakcontinue 与标签语句在嵌套循环中的作用
  • 掌握 try...catch...finally 异常处理机制
  • 通过猜数字游戏综合运用所有流程控制语句

6.1 条件语句:if...else 与 switch

条件语句让程序能够根据不同条件执行不同的代码分支。JavaScript 提供了两种条件语句:if...elseswitch

6.1.1 if...else 语句

if...else 是最基本、最常用的条件语句。

javascript 复制代码
const age = 18;

if (age >= 18) {
  console.log('成年人');
} else {
  console.log('未成年人');
}

多条件判断:else if

javascript 复制代码
const score = 85;

if (score >= 90) {
  console.log('优秀');
} else if (score >= 80) {
  console.log('良好');
} else if (score >= 60) {
  console.log('及格');
} else {
  console.log('不及格');
}

条件表达式中的隐式转换

if 语句会将条件表达式转换为布尔值。在第4章中我们已经学过 falsy 值,这里再强调一下常见的陷阱:

javascript 复制代码
// ✅ 正确:检查数组是否为空
const arr = [];
if (arr.length === 0) {
  console.log('空数组');
}

// ❌ 错误:空数组是 truthy
if (arr) {
  console.log('这行会执行,但 arr 可能是空数组');
}

// ✅ 正确:检查对象属性
const user = { name: 'Alice' };
if (user.age !== undefined) {
  console.log('有 age 属性');
}

// 更简洁的写法
if ('age' in user) {
  console.log('有 age 属性');
}

最佳实践 :条件表达式尽量使用显式比较(===!==><),避免依赖隐式转换,代码可读性和可维护性更高。

6.1.2 三元运算符:简洁的条件表达式

三元运算符 ? :if...else 的表达式形式,适合简单的条件赋值。

javascript 复制代码
const age = 20;
const status = age >= 18 ? '成年人' : '未成年人';
console.log(status); // "成年人"

链式三元运算符(不推荐过度使用):

javascript 复制代码
const score = 85;
const grade = score >= 90 ? 'A'
            : score >= 80 ? 'B'
            : score >= 60 ? 'C'
            : 'D';
console.log(grade); // "B"

使用建议 :三元运算符适合简单的条件赋值和函数参数。如果逻辑复杂,应使用 if...else,可读性更重要。

6.1.3 switch 语句

当需要对同一个值 进行多个等值判断时,switch 比一连串的 if...else if 更清晰。

javascript 复制代码
const day = new Date().getDay(); // 0-6,周日到周六

switch (day) {
  case 0:
    console.log('周日');
    break;
  case 1:
    console.log('周一');
    break;
  case 2:
    console.log('周二');
    break;
  case 3:
    console.log('周三');
    break;
  case 4:
    console.log('周四');
    break;
  case 5:
    console.log('周五');
    break;
  case 6:
    console.log('周六');
    break;
  default:
    console.log('未知');
}

switch 的穿透(Fall-through)特性

如果省略 break,代码会继续执行下一个 case ,这就是穿透。有时候可以利用这个特性,但更多时候忘记写 break 会导致 Bug。

javascript 复制代码
// 利用穿透:合并相同逻辑
const month = 3;
let season;

switch (month) {
  case 12:
  case 1:
  case 2:
    season = '冬季';
    break;
  case 3:
  case 4:
  case 5:
    season = '春季';
    break;
  case 6:
  case 7:
  case 8:
    season = '夏季';
    break;
  case 9:
  case 10:
  case 11:
    season = '秋季';
    break;
  default:
    season = '未知';
}
console.log(season); // "春季"

注意switch 使用 ===(严格相等)进行比较,不会发生类型转换。

javascript 复制代码
const value = '1';

switch (value) {
  case 1:
    console.log('数字 1'); // 不会执行
    break;
  case '1':
    console.log('字符串 "1"'); // ✅ 执行这个
    break;
}

6.1.4 if...else vs switch:如何选择?

场景 推荐语句 原因
范围判断(如 x > 10 if...else switch 只能做等值比较
对同一值的多个等值分支 switch 结构清晰,性能好
2-3 个简单分支 if...else 或三元 代码量少,无需 switch
需要复杂条件组合 if...else switch 不支持复杂表达式
分支超过 5 个且是等值判断 switch 或对象映射 可读性优于 if...else

对象映射替代方案(现代 JavaScript 推荐):

javascript 复制代码
// switch 写法
switch (status) {
  case 'pending':  return '等待中';
  case 'success':  return '成功';
  case 'error':    return '失败';
  default:         return '未知';
}

// 对象映射写法(更简洁)
const statusMap = {
  pending: '等待中',
  success: '成功',
  error:   '失败'
};
return statusMap[status] || '未知';

6.2 循环语句:for、while、do...while

循环语句用于重复执行一段代码。JavaScript 提供了三种基本循环和两种遍历循环。

6.2.1 for 循环

for 循环是最常用的循环,适合已知循环次数的场景。

javascript 复制代码
// 基本语法
for (初始化; 条件; 更新) {
  // 循环体
}

// 打印 0 到 4
for (let i = 0; i < 5; i++) {
  console.log(i);
}
// 输出:0 1 2 3 4

遍历数组

javascript 复制代码
const fruits = ['苹果', '香蕉', '橙子'];

for (let i = 0; i < fruits.length; i++) {
  console.log(`${i + 1}. ${fruits[i]}`);
}
// 输出:
// 1. 苹果
// 2. 香蕉
// 3. 橙子

for...of 循环(ES6,遍历可迭代对象):

javascript 复制代码
const fruits = ['苹果', '香蕉', '橙子'];

for (const fruit of fruits) {
  console.log(fruit);
}
// 输出:苹果、香蕉、橙子
javascript 复制代码
// for...of 也可以遍历字符串
for (const char of 'Hello') {
  console.log(char);
}
// 输出:H e l l o

for...in 循环(遍历对象的可枚举属性):

javascript 复制代码
const user = { name: 'Alice', age: 25, city: '北京' };

for (const key in user) {
  console.log(`${key}: ${user[key]}`);
}
// 输出:
// name: Alice
// age: 25
// city: 北京

注意for...in 会遍历原型链上的可枚举属性,通常配合 hasOwnProperty() 使用,或者用 Object.keys() 代替。对数组使用 for...in不推荐的做法。

javascript 复制代码
// ❌ 不推荐:对数组使用 for...in
const arr = [10, 20, 30];
for (const i in arr) {
  console.log(i);       // "0" "1" "2"(字符串类型!)
  console.log(arr[i]);  // 10 20 30
}

// ✅ 推荐:对数组使用 for...of
for (const value of arr) {
  console.log(value);   // 10 20 30
}

6.2.2 while 循环

while 循环适合不确定循环次数的场景,先判断条件,再执行循环体。

javascript 复制代码
let count = 0;

while (count < 5) {
  console.log(count);
  count++;
}
// 输出:0 1 2 3 4

经典应用:辗转相除法求最大公约数

javascript 复制代码
function gcd(a, b) {
  while (b !== 0) {
    const temp = b;
    b = a % b;
    a = temp;
  }
  return a;
}

console.log(gcd(48, 18)); // 6
console.log(gcd(100, 75)); // 25

6.2.3 do...while 循环

do...whilewhile 类似,但先执行一次循环体,再判断条件。保证循环体至少执行一次。

javascript 复制代码
let num;

do {
  num = Math.floor(Math.random() * 10);
  console.log(`随机数:${num}`);
} while (num !== 7);

console.log('终于摇到 7 了!');

经典应用:用户输入验证

javascript 复制代码
// 模拟 Node.js 命令行交互
const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

let input;

function askInput() {
  rl.question('请输入一个 1-10 的数字:', (answer) => {
    input = parseInt(answer);
    if (input >= 1 && input <= 10) {
      console.log(`你输入了:${input}`);
      rl.close();
    } else {
      console.log('输入无效,请重新输入');
      askInput();
    }
  });
}

askInput();

6.2.4 三种循环对比

特性 for while do...while
适用场景 已知循环次数 不确定次数,可能不执行 至少执行一次
条件检查时机 每次循环前 每次循环前 每次循环后
最少执行次数 0 次 0 次 1 次
循环变量管理 内置(初始化/更新) 需手动管理 需手动管理
典型用途 遍历数组、计数 搜索、收敛算法 输入验证、菜单

6.2.5 for...of 与 for...in 的区别

这是面试高频考点,必须清晰理解。
#mermaid-svg-O4gHnBegCe2JtvFo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O4gHnBegCe2JtvFo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O4gHnBegCe2JtvFo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O4gHnBegCe2JtvFo .error-icon{fill:#552222;}#mermaid-svg-O4gHnBegCe2JtvFo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O4gHnBegCe2JtvFo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O4gHnBegCe2JtvFo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O4gHnBegCe2JtvFo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O4gHnBegCe2JtvFo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O4gHnBegCe2JtvFo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O4gHnBegCe2JtvFo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O4gHnBegCe2JtvFo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O4gHnBegCe2JtvFo .marker.cross{stroke:#333333;}#mermaid-svg-O4gHnBegCe2JtvFo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O4gHnBegCe2JtvFo p{margin:0;}#mermaid-svg-O4gHnBegCe2JtvFo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-O4gHnBegCe2JtvFo .cluster-label text{fill:#333;}#mermaid-svg-O4gHnBegCe2JtvFo .cluster-label span{color:#333;}#mermaid-svg-O4gHnBegCe2JtvFo .cluster-label span p{background-color:transparent;}#mermaid-svg-O4gHnBegCe2JtvFo .label text,#mermaid-svg-O4gHnBegCe2JtvFo span{fill:#333;color:#333;}#mermaid-svg-O4gHnBegCe2JtvFo .node rect,#mermaid-svg-O4gHnBegCe2JtvFo .node circle,#mermaid-svg-O4gHnBegCe2JtvFo .node ellipse,#mermaid-svg-O4gHnBegCe2JtvFo .node polygon,#mermaid-svg-O4gHnBegCe2JtvFo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-O4gHnBegCe2JtvFo .rough-node .label text,#mermaid-svg-O4gHnBegCe2JtvFo .node .label text,#mermaid-svg-O4gHnBegCe2JtvFo .image-shape .label,#mermaid-svg-O4gHnBegCe2JtvFo .icon-shape .label{text-anchor:middle;}#mermaid-svg-O4gHnBegCe2JtvFo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-O4gHnBegCe2JtvFo .rough-node .label,#mermaid-svg-O4gHnBegCe2JtvFo .node .label,#mermaid-svg-O4gHnBegCe2JtvFo .image-shape .label,#mermaid-svg-O4gHnBegCe2JtvFo .icon-shape .label{text-align:center;}#mermaid-svg-O4gHnBegCe2JtvFo .node.clickable{cursor:pointer;}#mermaid-svg-O4gHnBegCe2JtvFo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-O4gHnBegCe2JtvFo .arrowheadPath{fill:#333333;}#mermaid-svg-O4gHnBegCe2JtvFo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-O4gHnBegCe2JtvFo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-O4gHnBegCe2JtvFo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O4gHnBegCe2JtvFo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-O4gHnBegCe2JtvFo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O4gHnBegCe2JtvFo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-O4gHnBegCe2JtvFo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-O4gHnBegCe2JtvFo .cluster text{fill:#333;}#mermaid-svg-O4gHnBegCe2JtvFo .cluster span{color:#333;}#mermaid-svg-O4gHnBegCe2JtvFo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-O4gHnBegCe2JtvFo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-O4gHnBegCe2JtvFo rect.text{fill:none;stroke-width:0;}#mermaid-svg-O4gHnBegCe2JtvFo .icon-shape,#mermaid-svg-O4gHnBegCe2JtvFo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O4gHnBegCe2JtvFo .icon-shape p,#mermaid-svg-O4gHnBegCe2JtvFo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-O4gHnBegCe2JtvFo .icon-shape .label rect,#mermaid-svg-O4gHnBegCe2JtvFo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O4gHnBegCe2JtvFo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-O4gHnBegCe2JtvFo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-O4gHnBegCe2JtvFo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 循环遍历
for...of
for...in
遍历值(Values)
遍历键(Keys)
适用于可迭代对象
适用于对象属性
数组、字符串、Map、Set
普通对象

特性 for...of for...in
遍历内容 值(value) 键(key)
适用对象 可迭代对象(有 Symbol.iterator 任意对象
原型链属性 不会遍历 会遍历(需用 hasOwnProperty 过滤)
遍历顺序 按迭代器定义的顺序 不保证顺序
推荐用于数组 ✅ 推荐 ❌ 不推荐
javascript 复制代码
const arr = ['a', 'b', 'c'];

// for...of → 遍历值
for (const item of arr) {
  console.log(item); // "a" "b" "c"
}

// for...in → 遍历键(索引)
for (const index in arr) {
  console.log(index); // "0" "1" "2"(注意是字符串)
}

// entries() → 同时获取键和值
for (const [index, value] of arr.entries()) {
  console.log(`${index}: ${value}`);
}

6.3 break、continue 与标签语句

breakcontinue 用于在循环内部控制执行流程。配合标签语句,可以在嵌套循环中实现更精确的跳转。

6.3.1 break:跳出循环

break 立即终止当前循环或 switch 语句,跳到循环之后的代码。

javascript 复制代码
// 在数组中查找第一个偶数
const numbers = [1, 3, 5, 4, 7, 8];
let firstEven = null;

for (const num of numbers) {
  if (num % 2 === 0) {
    firstEven = num;
    break; // 找到后立即停止循环
  }
}

console.log(firstEven); // 4

6.3.2 continue:跳过本次迭代

continue 跳过当前迭代,直接进入下一次循环。

javascript 复制代码
// 打印 1-10 中的奇数
for (let i = 1; i <= 10; i++) {
  if (i % 2 === 0) {
    continue; // 跳过偶数
  }
  console.log(i);
}
// 输出:1 3 5 7 9

6.3.3 标签语句(Label Statement)

在嵌套循环中,breakcontinue 只能作用于最内层循环。使用标签语句可以指定要跳出或跳过哪一层循环。

javascript 复制代码
// 标签语句语法:labelName: statement

// 在二维数组中查找目标值
const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

const target = 5;
let found = false;

outer: for (let i = 0; i < matrix.length; i++) {
  for (let j = 0; j < matrix[i].length; j++) {
    if (matrix[i][j] === target) {
      found = true;
      console.log(`找到 ${target},位置:[${i}][${j}]`);
      break outer; // 直接跳出外层循环
    }
  }
}

console.log(found); // true

continue 配合标签

javascript 复制代码
// 打印九九乘法表,跳过偶数行
outer: for (let i = 1; i <= 9; i++) {
  if (i % 2 === 0) {
    continue outer; // 跳过整个偶数行
  }

  let row = '';
  for (let j = 1; j <= i; j++) {
    row += `${j}×${i}=${i * j}\t`;
  }
  console.log(row);
}
// 只打印第 1、3、5、7、9 行

使用建议 :标签语句在深层嵌套循环中有用,但如果嵌套超过 3 层,建议将内层逻辑抽取为独立函数,用 return 替代 break label,代码可读性更好。


6.4 异常处理:try...catch...finally

程序运行时难免会遇到错误。JavaScript 提供了 try...catch...finally 语句来捕获和处理异常,避免程序因未处理的错误而崩溃。

6.4.1 基本语法

javascript 复制代码
try {
  // 可能抛出异常的代码
  const result = JSON.parse('这不是合法的 JSON');
} catch (error) {
  // 处理异常
  console.log('解析失败:', error.message);
} finally {
  // 无论是否异常都会执行
  console.log('清理工作完成');
}
// 输出:
// 解析失败: Unexpected token '这', "这不是合法的 JSON" is not valid JSON
// 清理工作完成

三个部分的作用

部分 是否必须 执行时机 典型用途
try ✅ 必须 始终执行 放置可能出错的代码
catch 至少一个 try 中抛出异常时 处理错误、记录日志
finally 可选 无论是否异常 资源清理、关闭连接

6.4.2 throw 语句

使用 throw 可以主动抛出异常。

javascript 复制代码
function divide(a, b) {
  if (b === 0) {
    throw new Error('除数不能为零');
  }
  return a / b;
}

try {
  console.log(divide(10, 0));
} catch (error) {
  console.log('错误:', error.message); // "错误: 除数不能为零"
}

throw 可以抛出任意值 (但推荐抛出 Error 对象):

javascript 复制代码
// ✅ 推荐:抛出 Error 对象(包含 stack trace)
throw new Error('Something went wrong');

// ✅ 可以抛出自定义对象
throw { code: 404, message: 'Not Found' };

// ❌ 不推荐:抛出原始值(没有堆栈信息)
throw 'something went wrong';

6.4.3 Error 对象

JavaScript 内置了多种错误类型:

javascript 复制代码
// 常见错误类型
try { undefinedVariable; } catch (e) { console.log(e.name); } // ReferenceError
try { null.property; } catch (e) { console.log(e.name); }     // TypeError
try { eval('%%%'); } catch (e) { console.log(e.name); }       // SyntaxError

// Error 对象的关键属性
const err = new Error('出错了');
console.log(err.name);     // "Error"
console.log(err.message);  // "出错了"
console.log(err.stack);    // 完整的调用栈信息

常见错误类型一览

错误类型 触发场景 示例
ReferenceError 引用不存在的变量 console.log(x)
TypeError 值的类型不正确 null.foo1()
SyntaxError 代码语法错误 eval('{')
RangeError 值超出范围 new Array(-1)
URIError URI 处理函数参数错误 decodeURIComponent('%')

6.4.4 自定义 Error 类

在实际项目中,自定义错误类型可以让错误处理更精确。

javascript 复制代码
class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = 'ValidationError';
    this.field = field;
  }
}

class NotFoundError extends Error {
  constructor(resource) {
    super(`${resource} 不存在`);
    this.name = 'NotFoundError';
    this.resource = resource;
  }
}

// 使用
function findUser(id) {
  if (id <= 0) {
    throw new ValidationError('id', 'ID 必须为正整数');
  }
  // 模拟查找
  throw new NotFoundError('User');
}

try {
  findUser(-1);
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(`验证失败 [${error.field}]: ${error.message}`);
  } else if (error instanceof NotFoundError) {
    console.log(`未找到: ${error.resource}`);
  } else {
    throw error; // 未知错误,继续向上抛出
  }
}
// 输出:验证失败 [id]: ID 必须为正整数

6.4.5 finally 的执行时机

finally无论 try 中是否发生异常都会执行 ,甚至在 trycatch 中有 return 语句时也不例外。

javascript 复制代码
function test() {
  try {
    console.log('try 执行');
    return 'from try';
  } catch (error) {
    return 'from catch';
  } finally {
    console.log('finally 执行'); // 即使 try 中有 return,也会执行
  }
}

console.log(test());
// 输出:
// try 执行
// finally 执行
// from try

关键点finally 中的 return覆盖 trycatch 中的 return。因此不建议在 finally 中使用 return

javascript 复制代码
function badExample() {
  try {
    return 'try 的返回值';
  } finally {
    return 'finally 的返回值'; // 覆盖了 try 的返回值
  }
}

console.log(badExample()); // "finally 的返回值"

6.4.6 try...catch 的常见应用场景

场景1:安全解析 JSON

javascript 复制代码
function safeParse(jsonString) {
  try {
    return JSON.parse(jsonString);
  } catch (error) {
    console.warn('JSON 解析失败:', error.message);
    return null;
  }
}

const data = safeParse('{"name": "Alice"}'); // { name: 'Alice' }
const bad = safeParse('invalid');            // null

场景2:带重试的网络请求

javascript 复制代码
async function fetchWithRetry(url, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      return await response.json();
    } catch (error) {
      console.warn(`第 ${attempt} 次请求失败: ${error.message}`);
      if (attempt === maxRetries) {
        throw new Error(`请求失败,已重试 ${maxRetries} 次`);
      }
      // 指数退避等待
      await new Promise(resolve =>
        setTimeout(resolve, 1000 * Math.pow(2, attempt))
      );
    }
  }
}

6.5 实战:猜数字游戏

让我们综合运用本章所有知识,实现一个完整的猜数字游戏。

6.5.1 需求分析

  • 程序随机生成一个 1-100 的整数
  • 玩家每次输入一个数字进行猜测
  • 程序提示"太大了"或"太小了"
  • 记录猜测次数,猜中后显示
  • 限制最多猜 10 次

6.5.2 命令行版本(Node.js)

javascript 复制代码
const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

function ask(question) {
  return new Promise((resolve) => {
    rl.question(question, (answer) => {
      resolve(answer);
    });
  });
}

async function guessingGame() {
  const secret = Math.floor(Math.random() * 100) + 1;
  const maxAttempts = 10;
  let attempts = 0;

  console.log('🎮 猜数字游戏!');
  console.log(`我想了一个 1-100 之间的整数,你有 ${maxAttempts} 次机会。`);
  console.log('---');

  while (attempts < maxAttempts) {
    attempts++;
    const input = await ask(`第 ${attempts} 次猜测(1-100):`);
    const guess = parseInt(input);

    // 输入验证
    if (isNaN(guess) || guess < 1 || guess > 100) {
      console.log('⚠️ 请输入 1-100 之间的整数!');
      attempts--; // 无效输入不计次数
      continue;
    }

    // 判断逻辑
    if (guess === secret) {
      console.log(`🎉 恭喜你!答案就是 ${secret},你用了 ${attempts} 次猜中!`);
      break;
    } else if (guess < secret) {
      console.log('📈 太小了,再大一点!');
    } else {
      console.log('📉 太大了,再小一点!');
    }

    // 提示剩余次数
    const remaining = maxAttempts - attempts;
    if (remaining > 0) {
      console.log(`(剩余 ${remaining} 次机会)`);
    }
  }

  // 超过最大次数
  if (attempts >= maxAttempts && guess !== secret) {
    console.log(`😢 次数用完了!答案是 ${secret}`);
  }

  rl.close();
}

guessingGame();

6.5.3 代码结构解析

#mermaid-svg-bEvtdbBXrhlVdajJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bEvtdbBXrhlVdajJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bEvtdbBXrhlVdajJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bEvtdbBXrhlVdajJ .error-icon{fill:#552222;}#mermaid-svg-bEvtdbBXrhlVdajJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bEvtdbBXrhlVdajJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bEvtdbBXrhlVdajJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bEvtdbBXrhlVdajJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bEvtdbBXrhlVdajJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bEvtdbBXrhlVdajJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bEvtdbBXrhlVdajJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bEvtdbBXrhlVdajJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bEvtdbBXrhlVdajJ .marker.cross{stroke:#333333;}#mermaid-svg-bEvtdbBXrhlVdajJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bEvtdbBXrhlVdajJ p{margin:0;}#mermaid-svg-bEvtdbBXrhlVdajJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bEvtdbBXrhlVdajJ .cluster-label text{fill:#333;}#mermaid-svg-bEvtdbBXrhlVdajJ .cluster-label span{color:#333;}#mermaid-svg-bEvtdbBXrhlVdajJ .cluster-label span p{background-color:transparent;}#mermaid-svg-bEvtdbBXrhlVdajJ .label text,#mermaid-svg-bEvtdbBXrhlVdajJ span{fill:#333;color:#333;}#mermaid-svg-bEvtdbBXrhlVdajJ .node rect,#mermaid-svg-bEvtdbBXrhlVdajJ .node circle,#mermaid-svg-bEvtdbBXrhlVdajJ .node ellipse,#mermaid-svg-bEvtdbBXrhlVdajJ .node polygon,#mermaid-svg-bEvtdbBXrhlVdajJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bEvtdbBXrhlVdajJ .rough-node .label text,#mermaid-svg-bEvtdbBXrhlVdajJ .node .label text,#mermaid-svg-bEvtdbBXrhlVdajJ .image-shape .label,#mermaid-svg-bEvtdbBXrhlVdajJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-bEvtdbBXrhlVdajJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bEvtdbBXrhlVdajJ .rough-node .label,#mermaid-svg-bEvtdbBXrhlVdajJ .node .label,#mermaid-svg-bEvtdbBXrhlVdajJ .image-shape .label,#mermaid-svg-bEvtdbBXrhlVdajJ .icon-shape .label{text-align:center;}#mermaid-svg-bEvtdbBXrhlVdajJ .node.clickable{cursor:pointer;}#mermaid-svg-bEvtdbBXrhlVdajJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bEvtdbBXrhlVdajJ .arrowheadPath{fill:#333333;}#mermaid-svg-bEvtdbBXrhlVdajJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bEvtdbBXrhlVdajJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bEvtdbBXrhlVdajJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bEvtdbBXrhlVdajJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bEvtdbBXrhlVdajJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bEvtdbBXrhlVdajJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bEvtdbBXrhlVdajJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bEvtdbBXrhlVdajJ .cluster text{fill:#333;}#mermaid-svg-bEvtdbBXrhlVdajJ .cluster span{color:#333;}#mermaid-svg-bEvtdbBXrhlVdajJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bEvtdbBXrhlVdajJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bEvtdbBXrhlVdajJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-bEvtdbBXrhlVdajJ .icon-shape,#mermaid-svg-bEvtdbBXrhlVdajJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bEvtdbBXrhlVdajJ .icon-shape p,#mermaid-svg-bEvtdbBXrhlVdajJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bEvtdbBXrhlVdajJ .icon-shape .label rect,#mermaid-svg-bEvtdbBXrhlVdajJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bEvtdbBXrhlVdajJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bEvtdbBXrhlVdajJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bEvtdbBXrhlVdajJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否



太小
太大


生成随机数 1-100
进入循环
读取用户输入
输入合法?
提示重新输入
猜中了?
恭喜 + 显示次数
太大还是太小?
提示太小
提示太大
还有剩余次数?
游戏结束 + 公布答案

这段代码用到了:

  • while 循环:控制游戏主循环
  • if...else if...else:判断猜测结果
  • break:猜中后跳出循环
  • continue:无效输入时跳过本次计数
  • parseInt()isNaN():输入验证与类型转换

6.5.4 进阶版本:二分查找策略

聪明的玩家会使用二分查找 策略,每次猜中间值,最多 7 次就能猜中 1-100 的任意数字(因为 2 7 = 128 > 100 2^7 = 128 > 100 27=128>100)。

javascript 复制代码
// 模拟二分查找策略的自动猜测
function autoGuess(min = 1, max = 100) {
  const secret = Math.floor(Math.random() * max) + min;
  let low = min;
  let high = max;
  let attempts = 0;

  console.log(`秘密数字已生成(${min}-${max})`);

  while (low <= high) {
    attempts++;
    const guess = Math.floor((low + high) / 2);

    if (guess === secret) {
      console.log(`第 ${attempts} 次:猜 ${guess} → 🎉 猜中了!`);
      break;
    } else if (guess < secret) {
      console.log(`第 ${attempts} 次:猜 ${guess} → 太小`);
      low = guess + 1;
    } else {
      console.log(`第 ${attempts} 次:猜 ${guess} → 太大`);
      high = guess - 1;
    }
  }

  console.log(`共用了 ${attempts} 次(最多 ${Math.ceil(Math.log2(max - min + 1))} 次)`);
}

autoGuess(); // 每次运行都不会超过 7 次

算法启示 :二分查找的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),对于 1-100 的范围,最多只需要 7 次比较。这体现了算法选择对效率的巨大影响。


6.6 常见面试题精讲

面试题 1:for...in 和 for...of 的区别是什么?

参考答案

  • for...in 遍历对象的可枚举属性名(键),会遍历原型链上的属性,适合遍历普通对象。
  • for...of 遍历可迭代对象的值 ,不会遍历原型链属性,适用于数组、字符串、Map、Set 等实现了 Symbol.iterator 接口的对象。
  • 对数组使用 for...in 是不推荐的,因为它返回的是字符串类型的索引,且可能遍历到被添加到原型上的属性。

面试题 2:以下代码输出什么?为什么?

javascript 复制代码
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}

答案 :输出 3 3 3

解析var 声明的 i 是函数作用域(或全局作用域),三次循环共享同一个 i 变量。当 setTimeout 回调执行时,循环已经结束,i 的值为 3。

修复方案

javascript 复制代码
// 方案1:使用 let(块级作用域)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0 1 2
}

// 方案2:使用闭包
for (var i = 0; i < 3; i++) {
  ((j) => {
    setTimeout(() => console.log(j), 100);
  })(i);
}

面试题 3:try...catch 能捕获所有错误吗?

参考答案

不能。try...catch 只能捕获同步代码中的运行时错误。以下情况无法捕获:

  1. 异步错误setTimeoutPromise 等异步回调中的错误。
  2. 语法错误 :代码解析阶段的 SyntaxErroreval 中的除外)。
javascript 复制代码
// ❌ 无法捕获异步错误
try {
  setTimeout(() => {
    throw new Error('异步错误'); // 不会被 catch 捕获
  }, 100);
} catch (error) {
  console.log('捕获不到');
}

// ✅ 异步错误需要单独处理
setTimeout(() => {
  try {
    throw new Error('异步错误');
  } catch (error) {
    console.log('捕获到了:', error.message);
  }
}, 100);

面试题 4:switch 使用 == 还是 === 比较?

答案switch 使用 ===(严格相等)进行比较,不会发生类型转换。

javascript 复制代码
const value = 0;

switch (value) {
  case false:
    console.log('false'); // 不会执行(0 !== false)
    break;
  case '':
    console.log('空字符串'); // 不会执行(0 !== '')
    break;
  case 0:
    console.log('零'); // ✅ 执行(0 === 0)
    break;
}

本章小结

知识点 要点
if...else 最通用的条件语句,支持范围判断和复杂条件
switch 适合对同一值的多个等值分支,使用 === 比较
三元运算符 简洁的条件表达式,适合简单赋值
for 循环 已知循环次数时首选,内置初始化/条件/更新
while 循环 不确定循环次数时首选,先判断后执行
do...while 保证至少执行一次,先执行后判断
for...of 遍历可迭代对象的,推荐用于数组
for...in 遍历对象的,不推荐用于数组
break 跳出当前循环或 switch
continue 跳过本次迭代,进入下一次
标签语句 在嵌套循环中指定 break/continue 的目标层
try...catch 捕获同步代码的运行时异常
finally 无论是否异常都执行,常用于资源清理
throw 主动抛出异常,推荐抛出 Error 对象

一句话总结 :流程控制是程序的"骨架"。if...elseswitch 决定走哪条路,forwhile 决定重复多少次,breakcontinue 控制循环的节奏,try...catch...finally 确保程序在出错时也能优雅地运行。


下篇预告第7章:函数基础 --- 我们将学习函数的声明方式、箭头函数、参数处理、高阶函数以及函数式编程的基本工具。


作者 :xhmico | CSDN 博客https://blog.csdn.net/xhmico

参考资源The Modern JavaScript Tutorial | MDN Web Docs | ECMAScript 规范

相关推荐
csj501 小时前
前端基础之《React(11)—state声明式》
前端·react.js
程序员晓琪1 小时前
深入解析:从 Webpack 到 Vite,多入口多端项目的工程化迁移实战
前端·webpack·vite
涛涛ing1 小时前
Vue 3.6 还没正式发布,但前端的方向已经被它定下来了
前端
用户938515635071 小时前
手写迷你 Cursor 终端:深入 Node.js child_process 的 spawn 核心实现
javascript·后端
阿新聊ai1 小时前
Claude Code 为什么总不懂你的项目?缺一张「项目地图」
前端·后端·面试
暗冰ཏོ1 小时前
Spring Boot + Vue3 订单支付模块技术实现:支付单、回调验签、幂等处理与余额扣减
前端·spring boot·后端·vue·微信支付·支付宝支付
Csvn1 小时前
AbortController 不止能取消 Fetch!3 个你不知道的隐藏用法
前端·javascript
大流星1 小时前
LangChainJS之Runnable(三)
javascript·langchain
foundbug9991 小时前
Polar Code 编解码 MATLAB 实现
开发语言·算法·matlab