JavaScript流程控制:if条件判断与for/while循环用法示例
深入讲解JavaScript流程控制核心语法,包含if条件分支与守卫语句、for循环生命周期、while与do...while循环机制及break/continue跳转用法,提供实用代码示例与专业图解。
if...else)与循环结构(for、while、do...while),通过具体的代码示例、执行步骤推导与选型依据,帮助读者掌握如何编写结构清晰、避免陷阱的流程控制逻辑。
一、条件分支:if、else if 与 else 的判断机制
条件判断语句允许程序在不同条件下执行不同的代码路径。其核心依托于括号内的表达式求值结果——在 JavaScript 运行时中,该表达式会被隐式转换为布尔值(Boolean)。
1. 基础语法与多分支结构
当只需要在条件成立时做处理,使用单个 if;当存在二选一逻辑时,搭配 else;面对多个互斥区间时,则使用 else if 进行串联阶梯判断。
function calculateDiscount(userLevel, cartAmount) {
let finalAmount = cartAmount;
if (userLevel === 'diamond') {
// 钻石会员享受 7 折
finalAmount = cartAmount * 0.7;
} else if (userLevel === 'gold') {
// 黄金会员享受 8.5 折
finalAmount = cartAmount * 0.85;
} else if (cartAmount >= 500) {
// 普通用户满 500 减 50
finalAmount = cartAmount - 50;
} else {
// 无优惠
finalAmount = cartAmount;
}
return finalAmount;
}
// 示例调用输出
console.log(calculateDiscount('gold', 1000)); // 输出示例:850
console.log(calculateDiscount('normal', 600)); // 输出示例:550
2. 隐式类型转换与真假值(Truthy / Falsy)
在 if (condition) 中,JavaScript 并不强制要求 condition 必须是严格的布尔类型。如果传入非布尔值,引擎会自动调用内部抽象操作将其转为布尔值。了解 JavaScript 中的八个“假值(Falsy)”至关重要:
false(布尔假)0、-0、0n(数值零与 BigInt 零)""(空字符串)null(空对象引用)undefined(未定义)NaN(非数值)
除上述假值外,所有其他值(包括空数组 []、空对象 {} 以及字符串 "0")均为真值(Truthy)。
if (count) 来判断数量是否传入,当 count = 0 时会被当成 false 误判为“未传入”。针对数值或配置项,应使用精准的比较运算符,例如:if (count !== undefined && count !== null)。
3. 进阶实践:用守卫语句(Guard Clause)消灭嵌套
在复杂业务逻辑中,多层嵌套的 if...else 会导致代码向右倾斜(被称为“回调地狱”或“箭头型代码”),极大增加了阅读和重构的难度。专业规范通常推荐使用守卫语句(提前返回)将异常边界在函数起始处剔除,保持核心主流程居左平铺。
// ❌ 反例:深度嵌套难以阅读
function processOrder(order) {
if (order) {
if (order.status === 'PAID') {
if (order.items.length > 0) {
return shipItems(order.items);
} else {
return '订单无商品';
}
} else {
return '订单尚未付款';
}
} else {
return '订单不存在';
}
}
// ✅ 推荐:扁平化守卫语句,提前返回
function processOrderClean(order) {
if (!order) return '订单不存在';
if (order.status !== 'PAID') return '订单尚未付款';
if (order.items.length === 0) return '订单无商品';
// 主干流程完全无缩进负担
return shipItems(order.items);
}
二、计数循环:for 循环的生命周期与执行步骤
当需要在进入循环前就能确定迭代次数,或者需要按特定步长访问数组索引时,标准 for 循环是最通用、执行效率最高的基础结构。
1. for 循环的四段式生命周期
标准 for 语句头部包含三个由分号分隔的表达式:for (初始化; 条件判断; 步进更新) { 循环体 }。它们在运行时的执行顺序严格遵循四步流水线:
- 初始化表达式(Initialization):在循环启动前执行一次,常用于使用
let声明循环变量。 - 条件判断表达式(Condition):在每轮迭代开始前求值。若计算结果为
true,进入循环体;若为false,彻底终止并退出循环。 - 循环体语句块(Statement Block):条件满足时执行的业务逻辑。
- 步进更新表达式(Final-expression):循环体执行完毕后执行,改变计数器状态,随后立即回到步骤 2 重新判断。
2. 典型实战用法与性能细节
示例 3:数组倒序遍历与长度缓存const productList = ['键盘', '鼠标', '显示器', '显卡', '主板'];
// 技巧 A:正序遍历时缓存数组长度,避免在每轮循环重复读取 length 属性
for (let i = 0, len = productList.length; i < len; i++) {
console.log(`位置 ${i + 1}:${productList[i]}`);
}
// 技巧 B:倒序遍历(适合在循环中需要安全执行 splice 删除元素的情景)
for (let i = productList.length - 1; i >= 0; i--) {
if (productList[i] === '显卡') {
console.log(`在索引 ${i} 处命中关键物料`);
}
}
for 中使用 var i = 0,由于 var 变量提升至函数或全局作用域,循环内若存在异步回调(如 setTimeout 或点击事件监听),所有回调访问的均是同一个最终状态的 i。而 let 具有块级作用域特性,JavaScript 引擎在每次迭代时都会为该轮循环创建独立的变量环境绑定,从而保证异步操作能准确捕获当前轮次的索引值。
三、状态驱动循环:while 与 do...while 循环
与按索引计数的 for 循环不同,while 和 do...while 循环通常用于事先无法预知具体需要执行多少轮、完全取决于某个动态状态演进或外部数据到来的场景。
1. while 循环:先验条件验证
while 循环在进入执行体之前必须先通过条件求值门禁。如果初始条件即为 false,循环体一次都不会运行。因此其最小执行次数为 0 次。
function binarySearch(sortedArr, target) {
let left = 0;
let right = sortedArr.length - 1;
// 依赖双指针状态夹逼,未知具体循环次数
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const midVal = sortedArr[mid];
if (midVal === target) {
return mid; // 命中目标直接返回索引
} else if (midVal < target) {
left = mid + 1; // 调整左边界
} else {
right = mid - 1; // 调整右边界
}
}
return -1; // 未检索到目标
}
const numbers = [12, 25, 33, 47, 59, 68, 81, 95];
console.log(binarySearch(numbers, 59)); // 输出示例:4
console.log(binarySearch(numbers, 100)); // 输出示例:-1
2. do...while 循环:后验条件保证
do...while 结构将判断语句放在循环体末尾。这意味着无论条件是否满足,循环体都无条件至少执行 1 次,随后再依据条件决定是否开启后续轮次。
function fetchWithRetry(maxRetries) {
let attempts = 0;
let success = false;
let result = null;
do {
attempts++;
console.log(`开始第 ${attempts} 次请求尝试...`);
// 模拟偶发性网络调用
const isMockSuccess = attempts === 3;
if (isMockSuccess) {
success = true;
result = '数据拉取成功';
}
} while (!success && attempts < maxRetries);
return { success, attempts, result };
}
// 示例调用输出
console.log(fetchWithRetry(5));
// 示例输出结果:{ success: true, attempts: 3, result: '数据拉取成功' }
四、流程干预与跳转:break 与 continue
在循环执行过程中,若满足特定前提,我们经常需要提前打断循环周期或跳过部分逻辑。JavaScript 提供了 break 与 continue 两个关键字来实现精细控制。
1. break:终止并跳出整个循环
break 语句会立即中断当前所在的整个循环结构,并将程序控制权移交给循环语句闭合大括号之后的下一条语句。常用于“寻找首个匹配元素后即刻停机”,避免无效空转。
2. continue:跳过当前轮次剩余代码
continue 并不会终止整个循环,而是仅放弃执行当前这一轮迭代中位于其后的语句,直接跳往步进更新阶段(在 for 中)或条件测试阶段(在 while 中),继而启动下一轮循环。
const rawPayloads = [
{ id: 101, valid: true, value: 50 },
{ id: 102, valid: false, value: 0 }, // 无效数据:需跳过
{ id: 103, valid: true, value: 120 },
{ id: 999, isEndSignal: true }, // 终止哨兵:需中断
{ id: 104, valid: true, value: 80 }
];
let totalValidScore = 0;
for (let i = 0; i < rawPayloads.length; i++) {
const item = rawPayloads[i];
// 遇到结束信号,立即彻底退出循环
if (item.isEndSignal) {
console.log(`在索引 ${i} 捕获到终止哨兵,停止处理。`);
break;
}
// 过滤无效数据项,提前进入下一轮
if (!item.valid) {
continue;
}
totalValidScore += item.value;
}
console.log(`有效积分统计总和: ${totalValidScore}`); // 输出示例:170
五、流程控制方案选型决策与避坑指南
在面对具体开发场景时,选择最贴合语义与性能要求的控制结构,能够使代码更具可读性并降低维护成本。下表汇总了各种语法的适用边界与推荐用法:
| 控制结构 | 先决条件 / 特征 | 最小执行轮次 | 最佳业务场景 |
|---|---|---|---|
| if...else if | 多条件布尔范围测试 | 根据匹配决定 | 业务规则判定、权限校验、参数区间划分 |
| for 循环 | 已知执行轮次、需要计数器 | 0 次 | 数组定长遍历、矩阵算法计算、多步步长递进 |
| while 循环 | 未知循环次数、依赖外部信号 | 0 次 | 二分收敛、轮询外部状态、游标分页拉取 |
| do...while | 必须无条件优先尝试执行一次 | 1 次 | 命令行用户交互、网络重试器、密码输入验证 |
| for...of | 直接访问可迭代对象的值 | 0 次 | 现代日常开发中遍历 Array、Set、Map 数据 |
常见易错陷阱与防护措施
-
死循环(Infinite Loop):编写
while或for循环时,必须确保循环体内存在能够推进条件向false演进的语句。对于复杂的异步轮询,始终建议加入硬性最大重试上限(如attempts < MAX_LIMIT)。 -
差一错误(Off-by-One Error):数组的合法索引范围为
0至arr.length - 1。正向遍历的标准边界判断应为i < arr.length,误写为i <= arr.length会导致最后一轮访问越界返回undefined。 -
在循环体中修改正在遍历的集合:在使用
for遍历数组并同时执行splice删除元素时,会导致后续元素的索引前移,造成元素遗漏。建议采用倒序遍历、或者使用不可变数组方法(如filter)生成新数组。
JS大数值转字符串出现科学计数法怎么办
2026-11-05 17:09:21
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38
怎么用JavaScript策略模式重构电商促销计算逻辑?
2026-11-05 14:01:09








