Skip to content

1.运算符

1.1 赋值运算符

已经学过的赋值运算符: = 将等号右边的值赋予给左边, 要求左边必须是一个容器
其他赋值运算符: +=-=*=/=%=

代码示例

javascript
//原始写法
let num = 1
num = num + 1
console.log(num)//输出结果是2

//简单写法
let num = 1
num += 1
console.log(num)//输出结果2

1.2 一元运算符

众多的JavaScript的运算符可以根据所需表达式的个数
分为一元运算符、二元运算符、三元运算符
二元运算符

javascript
let num = 10 + 20

一元运算符 正号

javascript
let num = +'123'

自增自减

javascript
//符号 ++ 作用 让变量值+1
//符号 -- 作用 让变量值-1

//前置自增
let num = 1
++num

//后置自增
let num1 = 1
num1++

注意:前置自增后后置自增单独运算没有区别,但是参与运算会有区别
前置自增: 先自加再使用++在前是前置自增

javascript
let i = 1
console.log(++i + 2)//结果是4 i是2 自增之后在进行运算

后置自增: 先运算后再自增++在后是后自增

javascript
let i = 1
console.log(i++ + 2)//结果是3 先和2相加运算输出完毕之后i再自加是2

面试题

javascript
let i = 1
//运算过程
//1. i++ 参与运算结果是1 i自增i + 1 = 2
//2. ++i i = 2 + 1 参与运算就是3 i = 3
//3. 所以运算结果是 1 + 3 + 3 = 7 i = 3
console.log(i++ + ++i + i)//结果是7
console.log(i)//结果是3

1.3 比较运算符

比较运算符用于比较两个值,并返回一个布尔值(truefalse)。

1.3.1 比较运算符表格

运算符名称作用示例结果说明
>大于判断左边是否大于右边5 > 3true数值比较
<小于判断左边是否小于右边5 < 3false数值比较
>=大于等于判断左边是否大于或等于右边5 >= 5true包含等于的情况
<=小于等于判断左边是否小于或等于右边5 <= 3false包含等于的情况
==相等(弱类型)判断两边值是否相等,会进行类型转换'5' == 5true自动类型转换
===全等(强类型)判断两边值和类型是否都相等'5' === 5false不进行类型转换,推荐使用
!=不相等(弱类型)判断两边值是否不相等,会进行类型转换'5' != 5false自动类型转换
!==不全等(强类型)判断两边值或类型是否不相等'5' !== 5true不进行类型转换,推荐使用

1.3.2 代码示例

javascript
// 1. 大于运算符 >
console.log(5 > 3)      // true
console.log(3 > 5)      // false

// 2. 小于运算符 <
console.log(5 < 3)      // false
console.log(3 < 5)      // true

// 3. 大于等于运算符 >=
console.log(5 >= 5)     // true
console.log(5 >= 3)     // true
console.log(3 >= 5)     // false

// 4. 小于等于运算符 <=
console.log(5 <= 5)     // true
console.log(3 <= 5)     // true
console.log(5 <= 3)     // false

// 5. 相等运算符 == (弱类型,会自动转换)
console.log(5 == 5)             // true
console.log('5' == 5)           // true  (字符串'5'转换为数字5)
console.log(true == 1)          // true  (true转换为1)
console.log(false == 0)         // true  (false转换为0)
console.log(null == undefined)  // true  (两者相等)
console.log('' == 0)            // true  (空字符串转换为0)

// 6. 全等运算符 === (强类型,不转换类型,推荐使用)
console.log(5 === 5)            // true
console.log('5' === 5)          // false (类型不同,字符串 vs 数字)
console.log(true === 1)         // false (类型不同,布尔 vs 数字)
console.log(null === undefined) // false (类型不同)

// 7. 不相等运算符 != (弱类型)
console.log(5 != 3)             // true
console.log('5' != 5)           // false (自动转换后相等)

// 8. 不全等运算符 !== (强类型,推荐使用)
console.log(5 !== 3)            // true
console.log('5' !== 5)          // true (类型不同)

1.3.3 重要注意事项

类型转换陷阱

比较表达式== 结果=== 结果说明
'' == 0truefalse空字符串转换为0
'0' == 0truefalse字符串'0'转换为0
false == 0truefalsefalse转换为0
true == 1truefalsetrue转换为1
null == undefinedtruefalse两者相等但类型不同
NaN == NaNfalsefalseNaN不等于任何值,包括自身

最佳实践

  • 推荐使用 ===!==:避免类型转换带来的意外结果
  • 避免使用 ==!=:除非有特殊需求需要类型转换
  • 注意 NaN 的特殊性:判断NaN需要使用 isNaN() 函数
  • 浮点数比较:0.1 + 0.2 === 0.3 返回 false(精度问题)

1.3.4 NaN 特殊情况

javascript
// NaN (Not a Number) 特殊情况
console.log(NaN == NaN)       // false  NaN不等于任何值
console.log(NaN === NaN)      // false  NaN不等于任何值
console.log(isNaN(NaN))       // true   使用isNaN判断NaN
console.log(isNaN('hello'))   // true   'hello'转换为数字为NaN

// 浮点数精度问题
console.log(0.1 + 0.2 === 0.3)  // false
console.log(0.1 + 0.2)          // 0.30000000000000004

// 正确的浮点数比较方法
const absDiff = Math.abs((0.1 + 0.2) - 0.3)
console.log(absDiff < 0.000001) // true  允许一定误差

1.3.5 实际应用场景

javascript
// 1. 条件判断
let age = 18
if (age >= 18) {
  console.log('已成年')
}

// 2. 数组筛选
let numbers = [1, 2, 3, 4, 5]
let bigNumbers = numbers.filter(num => num > 3)
console.log(bigNumbers)  // [4, 5]

// 3. 表单验证
let password = '123456'
if (password.length >= 6) {
  console.log('密码长度符合要求')
}

// 4. 循环控制
for (let i = 0; i < 5; i++) {
  console.log(i)  // 0, 1, 2, 3, 4
}

1.4 逻辑运算符

逻辑运算符用于对布尔值进行运算,返回的结果也是布尔值。在实际开发中常用于条件判断。

1.4.1 逻辑运算符表格

运算符名称作用示例结果说明
&&逻辑与(AND)两边都为true时返回truetrue && truetrue全真才真,一假即假
||逻辑或(OR)两边只要有一个为true就返回truetrue || falsetrue一真即真,全假才假
!逻辑非(NOT)取反,true变false,false变true!truefalse真变假,假变真

1.4.2 基础代码示例

javascript
// 1. 逻辑与 &&
// 全真才真,一假即假
console.log(true && true)    // true
console.log(true && false)   // false
console.log(false && true)   // false
console.log(false && false)  // false

// 2. 逻辑或 ||
// 一真即真,全假才假
console.log(true || true)    // true
console.log(true || false)   // true
console.log(false || true)   // true
console.log(false || false)  // false

// 3. 逻辑非 !
// 真变假,假变真
console.log(!true)   // false
console.log(!false)  // true
console.log(!!true)  // true   (双重否定,还原为true)

1.4.3 逻辑运算符的真值表

表达式A表达式BA && BA || B!A
truetruetruetruefalse
truefalsefalsetruefalse
falsetruefalsetruetrue
falsefalsefalsefalsetrue

1.4.4 逻辑运算符的短路特性

重要概念:逻辑运算符具有短路特性,会根据第一个表达式的值决定是否执行第二个表达式。

4.1 逻辑与 && 的短路特性
javascript
// 如果第一个表达式为false,则不会执行第二个表达式
// 因为结果注定为false

console.log(false && console.log('不会执行'))  // false
console.log(true && console.log('会执行'))     // 会执行

// 实际应用示例
let x = 10

// 如果x大于5,则执行函数
x > 5 && console.log('x大于5')  // 输出:x大于5

// 如果x不大于5,则不会执行
x > 20 && console.log('x大于20')  // 不输出
4.2 逻辑或 || 的短路特性
javascript
// 如果第一个表达式为true,则不会执行第二个表达式
// 因为结果注定为true

console.log(true || console.log('不会执行'))   // true
console.log(false || console.log('会执行'))    // 会执行

// 实际应用示例
let y = 10

// 如果y不大于20,则执行函数
y > 20 || console.log('y不大于20')  // 输出:y不大于20

// 如果y大于20,则不会执行
y > 5 || console.log('y不大于5')    // 不输出

1.4.5 逻辑运算符的返回值

注意:逻辑运算符返回的不一定是布尔值,而是参与运算的某个值。

javascript
// 逻辑与 &&:返回第一个为false的值,如果都为true则返回最后一个
console.log(0 && 1)        // 0  (第一个为false,直接返回)
console.log(1 && 2)        // 2  (都为true,返回最后一个)
console.log(1 && 0 && 2)   // 0  (返回第一个为false的值)
console.log(1 && 2 && 3)   // 3  (都为true,返回最后一个)

// 逻辑或 ||:返回第一个为true的值,如果都为false则返回最后一个
console.log(0 || 1)        // 1  (返回第一个为true的值)
console.log(0 || 0)        // 0  (都为false,返回最后一个)
console.log(1 || 2 || 0)   // 1  (返回第一个为true的值)
console.log(0 || 0 || 2)   // 2  (都为false才检查下一个)

// 逻辑非 !:总是返回布尔值
console.log(!'hello')       // false
console.log(!0)             // true
console.log(!'')            // true

1.4.6 常见实际应用场景

6.1 参数默认值设置
javascript
// 方法1:使用逻辑或设置默认值(传统方式)
function greet(name) {
  // 如果name为假值(null、undefined、''、0等),则使用'朋友'
  name = name || '朋友'
  console.log('你好,' + name)
}

greet()          // 输出:你好,朋友
greet('小明')    // 输出:你好,小明
greet('')        // 输出:你好,朋友(空字符串是假值)
6.2 多条件判断
javascript
// 场景1:用户登录验证
let username = 'admin'
let password = '123456'
let isActivated = true

// 只有用户名、密码都正确且账号已激活,才允许登录
if (username === 'admin' && password === '123456' && isActivated) {
  console.log('登录成功')
} else {
  console.log('登录失败')
}

// 场景2:权限检查
let role = 'admin'  // admin、editor、viewer

// admin 或 editor 可以编辑
if (role === 'admin' || role === 'editor') {
  console.log('有编辑权限')
}

// 场景3:表单验证
let username = ''
let email = 'test@example.com'

// 如果用户名或邮箱为空,提示错误
if (!username || !email) {
  console.log('用户名和邮箱不能为空')
}
6.3 简洁的条件执行
javascript
// 使用 && 简化条件判断
let age = 20

// 传统写法
if (age >= 18) {
  console.log('已成年')
}

// 简洁写法(只有条件为true时才执行)
age >= 18 && console.log('已成年')

// 使用 || 简化默认值处理
let userName = localStorage.getItem('user') || '游客'
console.log('欢迎,' + userName)
6.4 数组和对象的判断
javascript
// 判断数组是否有内容
let arr = [1, 2, 3]
if (arr && arr.length > 0) {
  console.log('数组不为空')
}

// 判断对象是否有属性
let obj = { name: '小明' }
if (obj && obj.name) {
  console.log('对象有name属性')
}

// 安全访问嵌套对象属性
let user = {
  profile: {
    name: '小明'
  }
}
// 只有user和user.profile都存在时才访问name
let name = user && user.profile && user.profile.name
console.log(name)  // '小明'

1.4.7 类型转换规则

在逻辑运算中,JavaScript会将值转换为布尔值:

类型转换为false的值转换为true的值
数字0NaN其他数字(如 1-13.14
字符串''(空字符串)非空字符串(如 'hello''0'
布尔值falsetrue
对象/数组-对象和数组总是true(即使是空对象/数组)
nullnull-
undefinedundefined-
javascript
// 假值
console.log(!0)           // true
console.log(!'')          // true
console.log(!false)       // true
console.log(!null)        // true
console.log(!undefined)   // true
console.log(!NaN)         // true

// 真值
console.log(!1)           // false
console.log(!'hello')     // false
console.log(!true)        // false
console.log(![])          // false  (空数组是真值)
console.log(!{})          // false  (空对象是真值)

1.4.8 面试题

javascript
// 面试题1
console.log(0 && 1)              // 0
console.log(1 && 0)              // 0
console.log(1 && 2 && 3)         // 3

// 面试题2
console.log(0 || 1)              // 1
console.log(1 || 0)              // 1
console.log(0 || 0 || 2)         // 2

// 面试题3:运算优先级
let a = 1, b = 2, c = 3
console.log(a > b && c > a)     // false  (先比较,再进行逻辑运算)
console.log(a < b || c > a)     // true

// 面试题4:复杂逻辑
let x = 5
console.log(x > 3 && x < 10 || x === 0)  // true
// 等价于:(x > 3 && x < 10) || x === 0
// 解释:先计算 &&(x > 3 && x < 10 = true),再计算 ||(true || false = true)

// 面试题5:短路求值
let y = 10
let z = (y > 5 && y++) || y
console.log(y, z)  // 11, 11
// 解释:y > 5为true,执行y++(y变为11),返回11,所以z=11

1.4.9 最佳实践

推荐做法

  • 使用 && 简化条件判断(condition && doSomething()
  • 使用 || 设置默认值(let val = input || 'default'
  • 使用 !! 将值转换为布尔值(let bool = !!value
  • 复杂条件建议使用括号提高可读性

注意事项

  • 注意短路特性,避免副作用
  • 理解逻辑运算符返回值不一定是布尔值
  • 使用 || 设置默认值时,假值(如 0'')会被替换
  • 对于更复杂的逻辑,考虑使用三元运算符或if语句

1.5 运算符优先级

运算符优先级决定了表达式中运算的执行顺序。优先级高的先执行,优先级低的后执行。相同优先级的运算符从左到右执行。

1.5.1 运算符优先级表格

优先级运算符说明结合性
1(最高). [] ()成员访问、数组下标、函数调用从左到右
2new创建对象从右到左
3++ --后置自增自减从左到右
4! ~ + - typeof void delete逻辑非、按位非、一元加减等从右到左
5**指数运算从右到左
6* / %乘、除、取余从左到右
7+ -加、减从左到右
8<< >> >>>左移、右移、无符号右移从左到右
9< <= > >=小于、小于等于、大于、大于等于从左到右
10== != === !==相等、不等、全等、不全等从左到右
11&按位与从左到右
12^按位异或从左到右
13|按位或从左到右
14&&逻辑与从左到右
15||逻辑或从左到右
16? :条件运算符(三元)从右到左
17= += -= *= /= %= **=赋值运算符从右到左
18(最低),逗号运算符从左到右

说明

  • 结合性:相同优先级的运算符,"从左到右"表示先算左边的,"从右到左"表示先算右边的
  • 优先级数字越小,优先级越高

1.5.2 常见优先级对比

优先级从高到低(常用运算符):

括号 ()                    最高
成员访问 . []
一元运算符 ! ++ -- typeof
乘除模 * / %
加减 + -
比较 < > <= >=
相等 == != === !==
逻辑与 &&
逻辑或 ||
赋值 = += -= ...
条件运算符 ? :
逗号运算符 ,               最低

1.5.3 代码示例

示例1:算术运算符优先级
javascript
// 乘除优先级高于加减
console.log(1 + 2 * 3)      // 7   (先算 2*3=6,再算 1+6=7)
console.log(10 - 2 * 3)     // 4   (先算 2*3=6,再算 10-6=4)
console.log(10 / 2 + 3)     // 8   (先算 10/2=5,再算 5+3=8)
console.log(10 / (2 + 3))   // 2   (括号优先,先算 2+3=5,再算 10/5=2)
示例2:比较运算符与逻辑运算符
javascript
// 比较运算符优先级高于逻辑运算符
console.log(1 < 2 && 2 < 3)     // true
// 等价于: (1 < 2) && (2 < 3)
//         true && true = true

console.log(1 > 2 || 2 < 3)     // true
// 等价于: (1 > 2) || (2 < 3)
//         false || true = true

console.log(1 === 1 && 2 === 2) // true
// 等价于: (1 === 1) && (2 === 2)
//         true && true = true
示例3:逻辑与 vs 逻辑或
javascript
// 逻辑与 && 优先级高于逻辑或 ||
console.log(true || false && false)  // true
// 等价于: true || (false && false)
//         true || false = true

console.log(false && false || true)  // true
// 等价于: (false && false) || true
//         false || true = true

console.log(true && true || false)  // true
// 等价于: (true && true) || false
//         true || false = true
示例4:赋值运算符优先级
javascript
// 赋值运算符优先级很低
let a = 2 + 3 * 4
console.log(a)  // 14  (先算 2+12=14,再赋值)

// 连续赋值
let x = y = 5
console.log(x, y)  // 5, 5
// 等价于: let x = (y = 5)

// 复合赋值
let b = 10
b += 2 * 3
console.log(b)  // 16
// 等价于: b = b + (2 * 3)
//         b = 10 + 6 = 16
示例5:自增自减与其他运算符
javascript
let a = 5
let b = a++ + 2
console.log(a, b)  // 6, 7
// 解释:先使用a的值(5)参与运算,所以b=5+2=7,然后a自增为6

let c = ++a + 2
console.log(a, c)  // 7, 9
// 解释:先a自增为7,然后使用a的值(7)参与运算,所以c=7+2=9
示例6:成员访问与函数调用
javascript
let obj = { name: '小明', age: 18 }

// 成员访问优先级高于赋值
let age = obj.age + 1
console.log(age)  // 19
// 等价于: let age = (obj.age) + 1

// 函数调用优先级最高
let result = Math.max(1, 2, 3) * 2
console.log(result)  // 6
// 等价于: let result = (Math.max(1, 2, 3)) * 2
//         let result = 3 * 2 = 6
示例7:三元运算符优先级
javascript
// 三元运算符优先级高于赋值
let score = 85
let level = score >= 90 ? '优秀' : score >= 60 ? '及格' : '不及格'
console.log(level)  // 及格
// 等价于: let level = (score >= 90) ? '优秀' : ((score >= 60) ? '及格' : '不及格')

// 三元运算符优先级低于逻辑运算符
let x = true && false ? 'A' : 'B'
console.log(x)  // B
// 等价于: let x = (true && false) ? 'A' : 'B'
//         let x = false ? 'A' : 'B' = 'B'

1.5.4 结合性示例

从左到右(多数运算符)
javascript
// 乘除、加减:从左到右
console.log(10 - 3 - 2)    // 5
// 等价于: (10 - 3) - 2 = 7 - 2 = 5

console.log(20 / 4 / 2)    // 2.5
// 等价于: (20 / 4) / 2 = 5 / 2 = 2.5

console.log(1 + 2 - 3 + 4)  // 4
// 等价于: ((1 + 2) - 3) + 4 = 4
从右到左(赋值、一元运算符等)
javascript
// 赋值运算符:从右到左
let a = b = c = 10
console.log(a, b, c)  // 10, 10, 10
// 等价于: let a = (b = (c = 10))

// 一元运算符:从右到左
let x = -+-5
console.log(x)  // 5
// 等价于: let x = -(-(-5)) = -(-5) = 5

// 指数运算:从右到左
console.log(2 ** 3 ** 2)  // 512
// 等价于: 2 ** (3 ** 2) = 2 ** 9 = 512

// 三元运算符:从右到左
let result = 1 ? 2 : 3 ? 4 : 5
console.log(result)  // 2
// 等价于: 1 ? 2 : (3 ? 4 : 5)
//         true ? 2 : 4 = 2

1.5.5 复杂表达式解析

javascript
// 示例1:综合运算
let result1 = 1 + 2 * 3 > 4 && 5 < 6
console.log(result1)  // true
// 解析步骤:
// 1. 2 * 3 = 6
// 2. 1 + 6 = 7
// 3. 7 > 4 = true
// 4. 5 < 6 = true
// 5. true && true = true

// 示例2:带括号的复杂表达式
let result2 = (1 + 2) * (3 + 4) > 10 && false || true
console.log(result2)  // true
// 解析步骤:
// 1. (1 + 2) = 3
// 2. (3 + 4) = 7
// 3. 3 * 7 = 21
// 4. 21 > 10 = true
// 5. true && false = false
// 6. false || true = true

// 示例3:混合运算
let a = 5
let result3 = a++ * 2 + 3 >= 10 && a < 10 || false
console.log(result3)  // true
// 解析步骤:
// 1. a++ 使用a的值5,然后a变为6
// 2. 5 * 2 = 10
// 3. 10 + 3 = 13
// 4. 13 >= 10 = true
// 5. 6 < 10 = true (注意:此时a已经是6)
// 6. true && true = true
// 7. true || false = true

1.5.6 面试题

javascript
// 面试题1
console.log(1 + '1')           // '11'   (字符串拼接,+两侧有字符串时)
console.log(1 - '1')           // 0      (减法会转为数字)
console.log(1 * '1')           // 1      (乘法会转为数字)
console.log(1 / '1')           // 1      (除法会转为数字)
console.log(1 + 1 + '1')       // '21'   (1+1=2,然后2+'1'='21')
console.log('1' + 1 + 1)       // '111'  ('1'+1='11',然后'11'+1='111')

// 面试题2
console.log(1 > 2 > 3)        // false
// 解析:(1 > 2) > 3
//       false > 3
//       0 > 3 = false

console.log(1 < 2 < 3)        // true
// 解析:(1 < 2) < 3
//       true < 3
//       1 < 3 = true

// 面试题3
let a = 1
let b = a++ + ++a + a
console.log(b)  // 6
// 解析:a=1
// 1. a++ 使用a的值1参与运算,然后a变为2
// 2. ++a a先自增为3,然后使用3参与运算
// 3. a 使用当前值3
// 4. 所以:1 + 3 + 3 = 7
// 等等,让我重新分析:
// a++时返回1,a变为2
// ++a时a变为3,返回3
// a是3
// 所以:1 + 3 + 3 = 7

// 面试题4
console.log(0 || 1 && 2)      // 2
// 解析:0 || (1 && 2)
//       0 || 2 = 2
// &&优先级高于||

// 面试题5
console.log(1 && 2 || 0)      // 2
// 解析:(1 && 2) || 0
//       2 || 0 = 2
// &&优先级高于||

// 面试题6
console.log(5 > 3 ? 2 : 3 + 4)  // 2
// 解析:(5 > 3) ? 2 : (3 + 4)
//       true ? 2 : 7 = 2

// 面试题7
let x = 1, y = 2
console.log(x += y += 3)      // 6
// 解析:x += (y += 3)
//       y += 3  => y = 2 + 3 = 5
//       x += y  => x = 1 + 5 = 6
// 返回6

// 面试题8
console.log(typeof typeof 1)  // 'string'
// 解析:typeof 1 = 'number'
//       typeof 'number' = 'string'

1.5.7 最佳实践

推荐做法

  1. 使用括号明确优先级
javascript
// 不推荐(依赖运算符优先级,可读性差)
let result = 1 + 2 * 3 > 4 && 5 < 6

// 推荐(使用括号,清晰明了)
let result = ((1 + 2) * 3 > 4) && (5 < 6)
  1. 复杂表达式拆分
javascript
// 不推荐
let score = (a + b) * (c - d) > e && f < g ? h : i

// 推荐
let left = (a + b) * (c - d)
let condition1 = left > e && f < g
let score = condition1 ? h : i
  1. 记住常用优先级
  • 括号 () 优先级最高
  • 一元运算符 ! ++ -- 较高
  • 乘除模 * / % 高于加减 + -
  • 比较运算符 > ==
  • 逻辑与 && 高于逻辑或 ||
  • 赋值运算符 = 优先级最低

注意事项

  • 不确定的优先级时,使用括号是最保险的做法
  • + 运算符:如果一侧是字符串,执行字符串拼接
  • 不要写出过于复杂的表达式,拆分为多行
  • 理解 && 优先级高于 ||,这个在条件判断中很重要
  • 记住赋值运算符是从右到左的结合性

1.5.8 快速参考表(简化版)

运算符类别运算符优先级(相对高低)
括号()最高
一元运算符! ++ -- typeof很高
指数**很高
乘除模* / %
加减+ -中高
比较> < >= <=
相等== != === !==
逻辑与&&中低
逻辑或||
赋值= += -=很低
逗号,最低

2.语句

2.1 表达式和语句

在JavaScript中,表达式(Expression)和语句(Statement)是两个重要的概念,理解它们的区别对于掌握JavaScript非常重要。

2.1.1 基本概念

什么是表达式?

表达式是可以被求值的代码片段,会产生一个值。表达式可以包含变量、运算符、函数调用等。

什么是语句?

语句是执行某种操作的代码指令,不会直接产生值。语句用于完成特定的任务,如声明变量、控制流程等。

2.1.2 表达式与语句对比表

特性表达式(Expression)语句(Statement)
定义可以被求值的代码片段执行某种操作的代码指令
返回值有返回值,计算后产生一个值无返回值,或返回undefined
用途用于计算、产生数据用于控制程序流程、执行操作
位置可以出现在任何需要值的地方必须独立作为一行或块执行
嵌套可以嵌套在其他表达式中通常不嵌套(语句块除外)
示例1 + 2x * yfn()ifforwhilevar

2.1.3 常见的表达式

算术表达式
javascript
// 基本算术运算
1 + 2           // 3
10 - 5          // 5
3 * 4           // 12
8 / 2           // 4

// 复杂表达式
(1 + 2) * 3     // 9
10 / 2 + 3      // 8
字符串表达式
javascript
'Hello' + ' World'      // 'Hello World'
'abc' + 123             // 'abc123'
逻辑表达式
javascript
true && false           // false
true || false           // true
!true                   // false
比较表达式
javascript
1 > 2                   // false
1 === 1                 // true
1 < 3 && 3 > 1          // true
变量表达式
javascript
let x = 10
x                       // 10
x + 5                   // 15
函数调用表达式
javascript
function add(a, b) {
  return a + b
}

add(1, 2)               // 3
Math.max(1, 2, 3)       // 3
console.log('hello')    // undefined (返回值是undefined)
对象和数组表达式
javascript
// 对象字面量
{ name: '小明', age: 18 }

// 数组字面量
[1, 2, 3, 4, 5]

// 属性访问表达式
let obj = { name: '小明' }
obj.name                // '小明'

// 数组元素访问表达式
let arr = [1, 2, 3]
arr[0]                  // 1
条件(三元)表达式
javascript
let age = 18
age >= 18 ? '成年' : '未成年'    // '成年'

let score = 85
score >= 90 ? '优秀' : score >= 60 ? '及格' : '不及格'  // '及格'
赋值表达式
javascript
let x
x = 10                  // 10 (赋值表达式返回赋的值)

let y
y = x = 5               // 5 (连续赋值)
console.log(x, y)       // 5, 5

// 复合赋值表达式
let a = 10
a += 5                  // 15 (返回新值)

2.1.4 常见的语句

变量声明语句
javascript
// 声明语句
let x = 10
const y = 20
var z = 30

// 注意:这些都是语句,不是表达式
// let x = 10 不能直接参与运算
// 错误示例:console.log(let x = 10)  // 报错
条件语句
javascript
// if 语句
let age = 18
if (age >= 18) {
  console.log('已成年')
} else {
  console.log('未成年')
}

// switch 语句
let day = 1
switch (day) {
  case 1:
    console.log('周一')
    break
  case 2:
    console.log('周二')
    break
  default:
    console.log('其他')
}
循环语句
javascript
// for 循环
for (let i = 0; i < 5; i++) {
  console.log(i)
}

// while 循环
let i = 0
while (i < 5) {
  console.log(i)
  i++
}

// do-while 循环
let j = 0
do {
  console.log(j)
  j++
} while (j < 5)

// for...in 循环
let obj = { name: '小明', age: 18 }
for (let key in obj) {
  console.log(key, obj[key])
}

// for...of 循环
let arr = [1, 2, 3]
for (let item of arr) {
  console.log(item)
}
控制语句
javascript
// break 语句
for (let i = 0; i < 10; i++) {
  if (i === 5) {
    break  // 退出循环
  }
  console.log(i)
}

// continue 语句
for (let i = 0; i < 10; i++) {
  if (i % 2 === 0) {
    continue  // 跳过本次循环
  }
  console.log(i)
}

// return 语句
function add(a, b) {
  return a + b  // 返回值并退出函数
}

// throw 语句
throw new Error('出错了')
空语句
javascript
// 空语句(只有一个分号)
;

// 实际应用
function doNothing() {
  // 空函数体
}
块语句
javascript
{
  let x = 10
  let y = 20
  console.log(x + y)
}

2.1.5 表达式与语句的区别实例

实例1:赋值语句 vs 赋值表达式
javascript
// 赋值语句
let x = 10          // 这是语句
// let x = 10 不能放在其他表达式中

// 赋值表达式
let y
y = 5               // 这是表达式,返回5
console.log(y = 5)  // 5 (输出赋值表达式的值)

// 连续赋值
let a, b
a = b = 10          // 赋值表达式从右到左执行
console.log(a, b)   // 10, 10
实例2:条件语句 vs 条件表达式
javascript
// if 语句(语句)
let score = 85
let level
if (score >= 90) {
  level = '优秀'
} else if (score >= 60) {
  level = '及格'
} else {
  level = '不及格'
}
console.log(level)  // 及格

// 三元表达式(表达式)
let level2 = score >= 90 ? '优秀' : score >= 60 ? '及格' : '不及格'
console.log(level2)  // 及格

// 三元表达式可以嵌套在其他表达式中
let result = `你的成绩是${score >= 90 ? '优秀' : score >= 60 ? '及格' : '不及格'}`
console.log(result)  // 你的成绩是及格

// if 语句不能嵌套在表达式中
// let result2 = `成绩${if (score >= 90) '优秀'}`  // 报错
实例3:函数声明语句 vs 函数表达式
javascript
// 函数声明(语句)
function greet1(name) {
  return '你好,' + name
}
console.log(greet1('小明'))  // 你好,小明

// 函数表达式
let greet2 = function(name) {
  return '你好,' + name
}
console.log(greet2('小明'))  // 你好,小明

// 箭头函数(本质是函数表达式)
let greet3 = (name) => '你好,' + name
console.log(greet3('小明'))  // 你好,小明

// 箭头函数可以直接作为表达式使用
console.log(((name) => '你好,' + name)('小红'))  // 你好,小红
实例4:对象字面量 vs 对象初始化语句
javascript
// 对象字面量(表达式)
let obj1 = { name: '小明', age: 18 }
console.log(obj1)

// 可以直接使用对象字面量
console.log({ name: '小红', age: 20 })

// 可以作为函数参数
function logPerson(person) {
  console.log(person.name, person.age)
}
logPerson({ name: '小李', age: 22 })  // 小李 22
实例5:立即执行函数表达式(IIFE)
javascript
// 立即执行函数表达式
(function() {
  console.log('立即执行')
})()

// 带参数的IIFE
let result = (function(a, b) {
  return a + b
})(1, 2)
console.log(result)  // 3

// 箭头函数版本的IIFE
(() => {
  console.log('箭头函数立即执行')
})()

2.1.6 实际应用场景

场景1:模板字符串中的使用
javascript
let name = '小明'
let age = 18

// 模板字符串中只能使用表达式
console.log(`姓名:${name},年龄:${age}`)           // 正确
console.log(`姓名:${name.toUpperCase()},年龄:${age + 1}`)  // 正确

// 错误示例:不能在模板字符串中使用语句
// console.log(`姓名:${let x = 10}`)  // 报错
场景2:函数参数
javascript
// 函数参数需要是表达式
function add(a, b) {
  return a + b
}

console.log(add(1 + 2, 3 * 4))      // 15 (传入表达式)
console.log(add(10, 20))            // 30 (传入变量)

// 错误示例:不能使用语句作为参数
// add(let x = 10, let y = 20)  // 报错
场景3:数组元素初始化
javascript
// 数组元素需要是表达式
let arr = [
  1 + 2,           // 3
  3 * 4,           // 12
  10 > 5,          // true
  'hello' + 'world'  // 'helloworld'
]
console.log(arr)  // [3, 12, true, 'helloworld']

// 错误示例:不能使用语句
// let arr2 = [let x = 10]  // 报错
场景4:对象属性值
javascript
// 对象属性值需要是表达式
let obj = {
  sum: 1 + 2,                    // 3
  product: 3 * 4,                // 12
  isTrue: 10 > 5,                // true
  greeting: 'hello' + ' world'   // 'hello world'
}
console.log(obj)  // { sum: 3, product: 12, isTrue: true, greeting: 'hello world' }

// 错误示例:不能使用语句
// let obj2 = { x: let y = 10 }  // 报错

2.1.7 面试题

javascript
// 面试题1:表达式 vs 语句
// 以下哪些是表达式,哪些是语句?

// 表达式:
1 + 2                    // ✓ 表达式
x * y                    // ✓ 表达式
x > 3                    // ✓ 表达式
fn()                     // ✓ 表达式
x = 5                    // ✓ 表达式
x >= 18 ? '成年' : '未成年'  // ✓ 表达式
{ name: '小明' }         // ✓ 表达式(对象字面量)
[1, 2, 3]               // ✓ 表达式(数组字面量)
function() {}           // ✓ 表达式(函数表达式)

// 语句:
let x = 10               // ✓ 语句
const y = 20             // ✓ 语句
if (x > 0) {}           // ✓ 语句
for (let i = 0; i < 10; i++) {}  // ✓ 语句
function fn() {}        // ✓ 语句(函数声明)
break                   // ✓ 语句
return                  // ✓ 语句
throw new Error()       // ✓ 语句

// 面试题2:以下代码的输出是什么?
let x = 10
console.log(x = 5)      // 5 (输出赋值表达式的值)
console.log(x)          // 5 (x已经被赋值为5)

// 面试题3:三元表达式 vs if 语句
let age = 20
let level1 = age >= 18 ? '成年' : '未成年'
console.log(level1)    // 成年

let level2
if (age >= 18) {
  level2 = '成年'
} else {
  level2 = '未成年'
}
console.log(level2)    // 成年

// 面试题4:函数声明 vs 函数表达式
// 函数声明会提升
console.log(fn1())     // fn1
function fn1() {
  return 'fn1'
}

// 函数表达式不会提升
// console.log(fn2())  // 报错:fn2 is not a function
let fn2 = function() {
  return 'fn2'
}
console.log(fn2())     // fn2

// 面试题5:在模板字符串中
let a = 10, b = 20
console.log(`结果:${a + b}`)    // 结果:30
console.log(`结果:${a > b}`)    // 结果:false

// 错误示例
// console.log(`结果:${let x = a + b}`)  // 报错

2.1.8 总结与最佳实践

关键要点

  1. 表达式产生值:表达式可以被求值,产生一个值
  2. 语句执行操作:语句用于执行操作,不直接产生值
  3. 表达式可以嵌套:表达式可以出现在任何需要值的地方
  4. 语句通常独立:语句通常作为独立的代码行或块执行

最佳实践

  1. 需要值的地方用表达式:函数参数、模板字符串、数组元素、对象属性值等
  2. 需要控制流程用语句:条件判断、循环、声明等
  3. 理解提升机制:函数声明会提升,函数表达式不会
  4. 合理使用三元表达式:简单的条件判断可以用三元表达式替代if语句
  5. 区分对象字面量和块语句
    javascript
    // 对象字面量(表达式)
    let obj = { name: '小明' }
    
    // 块语句(语句)
    {
      let x = 10
      console.log(x)
    }

注意事项

  • letconstvar 是语句,不能用在表达式中
  • ifforwhile 等是语句,不能嵌套在表达式中
  • 函数声明和函数表达式有不同的提升行为
  • 对象字面量在表达式中需要注意与块语句的区分

2.2 分支语句

分支语句用于根据条件控制程序的执行流程,JavaScript中主要的分支语句包括 if 语句、switch 语句和三元运算符。

2.2.1 if 单分支语句

if 单分支语句是最简单的条件判断语句,当条件为 true 时执行指定的代码块。

2.2.1.1 基本语法
javascript
if (条件) {
  // 当条件为 true 时执行的代码
}

语法说明

  • if:关键字
  • 条件:一个表达式,会被转换为布尔值
  • {}:代码块,包含要执行的语句
  • 当条件为 true 时,执行代码块中的代码
  • 当条件为 false 时,跳过代码块,继续执行后面的代码

2.2.1.2 代码示例
示例1:基础用法
javascript
// 判断年龄是否成年
let age = 18

if (age >= 18) {
  console.log('已成年')
}

// 判断数字是否为正数
let num = 5

if (num > 0) {
  console.log('这是一个正数')
}

// 判断字符串是否为空
let str = 'hello'

if (str.length > 0) {
  console.log('字符串不为空')
}
示例2:使用比较运算符
javascript
// 大于
let score = 85
if (score > 60) {
  console.log('成绩及格')
}

// 小于
let temperature = 25
if (temperature < 30) {
  console.log('温度适宜')
}

// 大于等于
let age = 18
if (age >= 18) {
  console.log('可以考驾照')
}

// 小于等于
let count = 10
if (count <= 100) {
  console.log('数量在范围内')
}

// 等于
let day = 1
if (day === 1) {
  console.log('今天是周一')
}

// 不等于
let status = 'active'
if (status !== 'inactive') {
  console.log('账号状态正常')
}
示例3:使用逻辑运算符
javascript
// 逻辑与 &&
let age = 20
let hasIdCard = true

if (age >= 18 && hasIdCard) {
  console.log('可以办理业务')
}

// 逻辑或 ||
let isMember = true
let hasCoupon = false

if (isMember || hasCoupon) {
  console.log('可以享受优惠')
}

// 逻辑非 !
let isRaining = false

if (!isRaining) {
  console.log('可以外出')
}
示例4:复杂条件
javascript
// 多个条件组合
let age = 25
let score = 85
let hasCertificate = true

if (age >= 18 && score >= 60 && hasCertificate) {
  console.log('符合录取条件')
}

// 嵌套比较
let x = 5
if (x > 0 && x < 10) {
  console.log('x 在 0 到 10 之间')
}

// 使用括号明确优先级
let a = 10
let b = 20
if ((a > 5 && b < 25) || a === 10) {
  console.log('条件满足')
}

2.2.1.3 执行流程
流程图:
┌─────────┐
│  开始    │
└────┬────┘


┌─────────┐
│ 判断条件 │
└────┬────┘

     ├─── true ───┐
     │            ▼
     │      ┌──────────┐
     │      │ 执行代码  │
     │      └────┬─────┘
     │           │
     │           ▼
     │      ┌──────────┐
     │      │ 跳出代码  │
     │      └────┬─────┘
     │           │
     │           │
     │           │
     │           │
     └── false ──┤


             ┌──────────┐
             │ 继续执行 │
             └──────────┘

2.2.1.4 实际应用场景
场景1:表单验证
javascript
// 用户名验证
let username = 'admin'
if (username.length >= 3) {
  console.log('用户名长度符合要求')
}

// 密码验证
let password = '123456'
if (password.length >= 6) {
  console.log('密码长度符合要求')
}

// 邮箱验证
let email = 'test@example.com'
if (email.includes('@') && email.includes('.')) {
  console.log('邮箱格式正确')
}
场景2:权限检查
javascript
// 检查管理员权限
let role = 'admin'
if (role === 'admin') {
  console.log('拥有管理员权限')
}

// 检查是否登录
let isLoggedIn = true
if (isLoggedIn) {
  console.log('用户已登录')
}

// 检查年龄限制
let userAge = 20
if (userAge >= 18) {
  console.log('可以访问成人内容')
}
场景3:数值处理
javascript
// 处理正数
let num = 10
if (num > 0) {
  console.log('这是一个正数')
}

// 处理偶数
let number = 8
if (number % 2 === 0) {
  console.log('这是一个偶数')
}

// 处理闰年
let year = 2024
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
  console.log('是闰年')
}
场景4:数据判断
javascript
// 判断数组是否有元素
let arr = [1, 2, 3]
if (arr.length > 0) {
  console.log('数组不为空')
}

// 判断对象是否有属性
let obj = { name: '小明' }
if (obj.name) {
  console.log('对象有 name 属性')
}

// 判断字符串是否包含特定内容
let text = 'Hello World'
if (text.includes('Hello')) {
  console.log('字符串包含 Hello')
}

2.2.1.5 注意事项

重要注意事项

  1. 代码块建议使用花括号
javascript
// 推荐:始终使用花括号
if (age >= 18) {
  console.log('已成年')
}

// 不推荐:省略花括号(只能控制一行)
if (age >= 18)
  console.log('已成年')

// 危险:省略花括号可能导致bug
if (age >= 18)
  console.log('已成年')
  console.log('可以投票')  // 这行会无条件执行!
  1. 条件中的类型转换
javascript
// 以下条件都会被转换为 true
if (1) { }                    // true
if ('hello') { }             // true
if ([1, 2, 3]) { }           // true
if ({ name: '小明' }) { }     // true

// 以下条件都会被转换为 false
if (0) { }                    // false
if ('') { }                   // false
if (null) { }                 // false
if (undefined) { }            // false
if (NaN) { }                  // false
  1. 使用严格相等
javascript
// 推荐:使用 ===
let num = '5'
if (num === 5) {
  console.log('数字是5')
}

// 不推荐:使用 ==
if (num == 5) {
  console.log('数字是5')  // 字符串'5'会被转换为数字5
}
  1. 复杂的条件使用括号
javascript
// 推荐:使用括号提高可读性
if ((age >= 18 && hasIdCard) || isVIP) {
  console.log('可以进入')
}

// 不推荐:可读性差
if (age >= 18 && hasIdCard || isVIP) {
  console.log('可以进入')
}

2.2.1.6 面试题
javascript
// 面试题1:以下代码输出什么?
let x = 10
if (x > 5) {
  console.log('A')
}
if (x > 0) {
  console.log('B')
}
if (x > 15) {
  console.log('C')
}
// 输出:A B
// 解释:x > 5 为 true,输出 A
//       x > 0 为 true,输出 B
//       x > 15 为 false,不输出

// 面试题2:以下代码输出什么?
let y = 5
if (y = 10) {  // 注意:这里用 = 不是 ==
  console.log('y 被赋值了')
}
console.log(y)
// 输出:
// y 被赋值了
// 10
// 解释:y = 10 是赋值表达式,返回 10(true),所以会执行代码块

// 面试题3:以下代码输出什么?
let a = 0
if (a) {
  console.log('A')
} else {
  console.log('B')
}
// 输出:B
// 解释:0 是假值,条件为 false

// 面试题4:以下代码输出什么?
let str = ''
if (str.length) {
  console.log('字符串不为空')
}
console.log(str.length)
// 输出:0
// 解释:空字符串的长度是 0(假值),不会执行 if 块

// 面试题5:以下代码输出什么?
let arr = []
if (arr.length) {
  console.log('数组不为空')
}
console.log(arr.length)
// 输出:0
// 解释:空数组的长度是 0(假值),不会执行 if 块

// 面试题6:以下代码输出什么?
let obj = {}
if (obj) {
  console.log('对象是true')
}
// 输出:对象是true
// 解释:空对象是真值,条件为 true

// 面试题7:以下代码输出什么?
let num = '10'
if (num === 10) {
  console.log('A')
}
if (num == 10) {
  console.log('B')
}
// 输出:B
// 解释:num === 10 为 false(类型不同)
//       num == 10 为 true(类型转换)

2.2.1.7 最佳实践

推荐做法

  1. 始终使用花括号
javascript
// 推荐
if (condition) {
  // 代码
}
  1. 使用严格相等
javascript
// 推荐
if (age === 18) { }

// 不推荐
if (age == 18) { }
  1. 复杂条件使用括号
javascript
// 推荐
if ((a > 0 && b < 10) || c === 5) { }
  1. 有意义的变量名
javascript
// 推荐
let isAdult = age >= 18
if (isAdult) {
  console.log('已成年')
}
  1. 将复杂条件提取为变量
javascript
// 推荐
let isValid = username.length >= 3 && password.length >= 6
if (isValid) {
  console.log('验证通过')
}

避免做法

  1. 避免在条件中赋值
javascript
// 错误:混淆了赋值和比较
if (x = 10) { }  // 应该用 ===

// 正确
if (x === 10) { }
  1. 避免过长的条件
javascript
// 不推荐
if (age >= 18 && hasIdCard && hasCertificate && score >= 60) { }

// 推荐
let meetsRequirements = age >= 18 && hasIdCard && hasCertificate && score >= 60
if (meetsRequirements) { }
  1. 避免嵌套过深
javascript
// 不推荐:嵌套过深
if (condition1) {
  if (condition2) {
    if (condition3) {
      // 代码
    }
  }
}

// 推荐:提前返回
if (!condition1) return
if (!condition2) return
if (!condition3) return
// 代码

2.2.1.8 总结

单分支语句要点

  1. 语法if (条件) { 代码块 }
  2. 执行:条件为 true 时执行代码块
  3. 注意:始终使用花括号,避免缩进错误
  4. 推荐:使用严格相等 ===,复杂条件使用括号
  5. 最佳实践:将复杂条件提取为变量,提高可读性

2.2.2 if 双分支语句(if...else)

if 双分支语句在单分支的基础上增加了 else 部分,当条件为 false 时执行 else 后面的代码块。

2.2.2.1 基本语法
javascript
if (条件) {
  // 当条件为 true 时执行的代码
} else {
  // 当条件为 false 时执行的代码
}

语法说明

  • if:关键字,表示条件判断
  • 条件:一个表达式,会被转换为布尔值
  • else:关键字,表示否则的情况
  • {}:代码块,包含要执行的语句
  • 当条件为 true 时,执行 if 代码块
  • 当条件为 false 时,执行 else 代码块
  • if 和 else 代码块只会执行其中一个

2.2.2.2 代码示例
示例1:基础用法
javascript
// 判断年龄是否成年
let age = 18

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

// 判断数字是否为正数
let num = 5

if (num > 0) {
  console.log('这是一个正数')
} else {
  console.log('这不是一个正数')
}

// 判断字符串是否为空
let str = 'hello'

if (str.length > 0) {
  console.log('字符串不为空')
} else {
  console.log('字符串为空')
}
示例2:成绩判断
javascript
// 简单判断及格
let score = 85

if (score >= 60) {
  console.log('及格')
} else {
  console.log('不及格')
}

// 输出:及格

// 不及格的情况
let score2 = 55

if (score2 >= 60) {
  console.log('及格')
} else {
  console.log('不及格')
}

// 输出:不及格
示例3:登录判断
javascript
// 用户登录
let isLoggedIn = true

if (isLoggedIn) {
  console.log('欢迎回来!')
} else {
  console.log('请先登录')
}

// 输出:欢迎回来!

// 未登录情况
let isLoggedIn2 = false

if (isLoggedIn2) {
  console.log('欢迎回来!')
} else {
  console.log('请先登录')
}

// 输出:请先登录
示例4:数字奇偶判断
javascript
// 判断奇偶数
let number = 7

if (number % 2 === 0) {
  console.log('偶数')
} else {
  console.log('奇数')
}

// 输出:奇数
示例5:年龄阶段判断
javascript
// 判断是否成年
let age = 16

if (age >= 18) {
  console.log('可以考驾照')
} else {
  console.log('还未到考驾照年龄')
}

// 输出:还未到考驾照年龄

2.2.2.3 执行流程
流程图:
┌─────────┐
│  开始    │
└────┬────┘


┌─────────┐
│ 判断条件 │
└────┬────┘

     ├─── true ───┐
     │            ▼
     │      ┌──────────┐
     │      │ 执行if   │
     │      │ 代码块   │
     │      └────┬─────┘
     │           │
     │           ▼
     │      ┌──────────┐
     │      │ 跳出分支 │
     │      └────┬─────┘
     │           │
     │           │
     │           ▼
     │      ┌──────────┐
     │           │ 继续执行 │
     │           └────┬─────┘
     │                │
     │                │
     └── false ────┤


               ┌──────────┐
               │ 执行else │
               │ 代码块   │
               └────┬─────┘


               ┌──────────┐
               │ 跳出分支 │
               └────┬─────┘


               ┌──────────┐
               │ 继续执行 │
               └──────────┘

2.2.2.4 实际应用场景
场景1:表单验证
javascript
// 用户名验证
let username = 'admin'

if (username.length >= 3) {
  console.log('用户名长度符合要求')
} else {
  console.log('用户名长度太短')
}

// 密码验证
let password = '123'

if (password.length >= 6) {
  console.log('密码长度符合要求')
} else {
  console.log('密码长度不足6位')
}

// 邮箱验证
let email = 'test@example.com'

if (email.includes('@') && email.includes('.')) {
  console.log('邮箱格式正确')
} else {
  console.log('邮箱格式不正确')
}
场景2:权限控制
javascript
// 检查管理员权限
let role = 'user'

if (role === 'admin') {
  console.log('拥有管理员权限,可以访问管理页面')
} else {
  console.log('只有普通用户权限')
}

// 检查是否登录
let isLoggedIn = false

if (isLoggedIn) {
  console.log('用户已登录,可以访问个人中心')
} else {
  console.log('用户未登录,跳转到登录页面')
}
场景3:折扣计算
javascript
// 会员折扣
let isMember = true
let price = 100

if (isMember) {
  console.log('会员价:' + (price * 0.8))
} else {
  console.log('原价:' + price)
}

// 输出:会员价:80

// 非会员情况
let isMember2 = false
let price2 = 100

if (isMember2) {
  console.log('会员价:' + (price2 * 0.8))
} else {
  console.log('原价:' + price2)
}

// 输出:原价:100
场景4:闰年判断
javascript
// 判断是否为闰年
let year = 2024

if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
  console.log(year + '年是闰年')
} else {
  console.log(year + '年不是闰年')
}

// 输出:2024年是闰年
场景5:温度提示
javascript
// 温度提示
let temperature = 35

if (temperature > 30) {
  console.log('天气很热,注意防暑')
} else {
  console.log('天气适宜')
}

// 输出:天气很热,注意防暑

2.2.2.5 双分支 vs 单分支
javascript
// 单分支 if:只处理 true 的情况
let age1 = 20

if (age1 >= 18) {
  console.log('已成年')
}
// 当 age1 < 18 时,什么都不会输出

// 双分支 if-else:处理 true 和 false 两种情况
let age2 = 20

if (age2 >= 18) {
  console.log('已成年')
} else {
  console.log('未成年')
}
// 无论 age2 是什么值,都会有输出

// 使用场景:
// - 单分支:只需要处理特定条件满足时的情况
// - 双分支:需要处理条件满足和不满足两种情况

2.2.2.6 注意事项

重要注意事项

  1. else 不能单独使用
javascript
// 错误:else 不能单独存在
else {
  console.log('错误')
}

// 正确:else 必须跟在 if 后面
if (condition) {
  console.log('条件成立')
} else {
  console.log('条件不成立')
}
  1. else 后面的条件不需要再次判断
javascript
// 不推荐:冗余的判断
let age = 20

if (age >= 18) {
  console.log('已成年')
} else if (age < 18) {  // 冗余!else 已经隐含了 age < 18
  console.log('未成年')
}

// 推荐:简洁的写法
if (age >= 18) {
  console.log('已成年')
} else {
  console.log('未成年')
}
  1. 代码块建议使用花括号
javascript
// 推荐:始终使用花括号
if (age >= 18) {
  console.log('已成年')
} else {
  console.log('未成年')
}

// 不推荐:省略花括号
if (age >= 18)
  console.log('已成年')
else
  console.log('未成年')
  1. if 和 else 只会执行其中一个
javascript
let x = 10

if (x > 5) {
  console.log('A')
} else {
  console.log('B')
}

// 只会输出 A,不会输出 B

2.2.2.7 面试题
javascript
// 面试题1:以下代码输出什么?
let a = 10

if (a > 5) {
  console.log('A')
} else {
  console.log('B')
}
// 输出:A
// 解释:a > 5 为 true,执行 if 代码块

// 面试题2:以下代码输出什么?
let b = 3

if (b > 5) {
  console.log('A')
} else {
  console.log('B')
}
// 输出:B
// 解释:b > 5 为 false,执行 else 代码块

// 面试题3:以下代码输出什么?
let c = 5

if (c >= 5) {
  console.log('A')
} else {
  console.log('B')
}
// 输出:A
// 解释:c >= 5 为 true,执行 if 代码块

// 面试题4:以下代码输出什么?
let num = 0

if (num) {
  console.log('A')
} else {
  console.log('B')
}
// 输出:B
// 解释:0 是假值,条件为 false,执行 else

// 面试题5:以下代码输出什么?
let str = ''

if (str.length) {
  console.log('字符串不为空')
} else {
  console.log('字符串为空')
}
// 输出:字符串为空
// 解释:空字符串的长度是 0(假值)

// 面试题6:以下代码输出什么?
let arr = []

if (arr.length) {
  console.log('数组不为空')
} else {
  console.log('数组为空')
}
// 输出:数组为空
// 解释:空数组的长度是 0(假值)

// 面试题7:以下代码输出什么?
let x = 10

if (x === 10) {
  console.log('A')
} else {
  console.log('B')
}
// 输出:A
// 解释:x === 10 为 true

let y = '10'

if (y === 10) {
  console.log('C')
} else {
  console.log('D')
}
// 输出:D
// 解释:'10' === 10 为 false(类型不同)

// 面试题8:以下代码输出什么?
let score = 75

if (score >= 90) {
  console.log('优秀')
} else {
  console.log('非优秀')
}

// 输出:非优秀
// 解释:75 >= 90 为 false

2.2.2.8 最佳实践

推荐做法

  1. 始终使用花括号
javascript
// 推荐
if (condition) {
  // 代码块1
} else {
  // 代码块2
}
  1. 使用严格相等
javascript
// 推荐
if (age === 18) {
  // 代码
} else {
  // 代码
}

// 不推荐
if (age == 18) {
  // 代码
} else {
  // 代码
}
  1. 有意义的条件表达式
javascript
// 推荐:使用有意义的变量名
let isAdult = age >= 18

if (isAdult) {
  console.log('已成年')
} else {
  console.log('未成年')
}
  1. 将复杂条件提取为变量
javascript
// 推荐
let isValid = username.length >= 3 && password.length >= 6

if (isValid) {
  console.log('验证通过')
} else {
  console.log('验证失败')
}
  1. else 块不应该重复判断条件
javascript
// 推荐
if (score >= 60) {
  console.log('及格')
} else {
  console.log('不及格')
}

// 不推荐
if (score >= 60) {
  console.log('及格')
} else if (score < 60) {  // 冗余
  console.log('不及格')
}

避免做法

  1. 避免在条件中赋值
javascript
// 错误:混淆了赋值和比较
if (x = 10) {
  console.log('A')
} else {
  console.log('B')
}

// 正确
if (x === 10) {
  console.log('A')
} else {
  console.log('B')
}
  1. 避免嵌套过深
javascript
// 不推荐:嵌套过深
if (condition1) {
  if (condition2) {
    if (condition3) {
      // 代码
    } else {
      // 代码
    }
  } else {
    // 代码
  }
}

// 推荐:使用提前返回或多分支
if (!condition1) return
if (!condition2) return
if (!condition3) return
// 代码
  1. 避免冗余的条件
javascript
// 不推荐:else 中重复判断
if (age >= 18) {
  console.log('已成年')
} else if (age < 18) {  // 冗余
  console.log('未成年')
}

// 推荐
if (age >= 18) {
  console.log('已成年')
} else {
  console.log('未成年')
}

2.2.3 if 多分支语句(if...else if...else)

if 多分支语句用于处理多个不同的条件情况,根据不同的条件执行对应的代码块。

2.2.3.1 基本语法
javascript
if (条件1) {
  // 当条件1为 true 时执行的代码
} else if (条件2) {
  // 当条件1为 false 且条件2为 true 时执行的代码
} else if (条件3) {
  // 当条件1、条件2都为 false 且条件3为 true 时执行的代码
} else {
  // 当所有条件都为 false 时执行的代码(可选)
}

语法说明

  • if:第一个条件判断
  • else if:额外的条件判断,可以有多个
  • else:所有条件都不满足时的处理(可选)
  • 执行顺序:从上到下依次判断,执行第一个为 true 的条件对应的代码块
  • 执行完成后,跳过剩余的所有条件判断

2.2.3.2 代码示例
示例1:成绩等级判断
javascript
// 判断成绩等级
let score = 85

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

// 输出:良好

// 其他情况测试
let score2 = 95
if (score2 >= 90) {
  console.log('优秀')
} else if (score2 >= 80) {
  console.log('良好')
} else if (score2 >= 70) {
  console.log('中等')
} else if (score2 >= 60) {
  console.log('及格')
} else {
  console.log('不及格')
}
// 输出:优秀
示例2:年龄段判断
javascript
// 判断年龄段
let age = 25

if (age < 6) {
  console.log('儿童')
} else if (age < 12) {
  console.log('少年')
} else if (age < 18) {
  console.log('青少年')
} else if (age < 30) {
  console.log('青年')
} else if (age < 50) {
  console.log('中年')
} else {
  console.log('老年')
}

// 输出:青年
示例3:时间问候
javascript
// 根据时间输出问候语
let hour = 14

if (hour >= 5 && hour < 12) {
  console.log('早上好')
} else if (hour >= 12 && hour < 14) {
  console.log('中午好')
} else if (hour >= 14 && hour < 18) {
  console.log('下午好')
} else if (hour >= 18 && hour < 22) {
  console.log('晚上好')
} else {
  console.log('夜深了,注意休息')
}

// 输出:下午好
示例4:BMI 指数计算
javascript
// 计算并判断BMI指数
let height = 1.75  // 身高(米)
let weight = 65    // 体重(公斤)
let bmi = weight / (height * height)

if (bmi < 18.5) {
  console.log('偏瘦,BMI:' + bmi.toFixed(2))
} else if (bmi < 24) {
  console.log('正常,BMI:' + bmi.toFixed(2))
} else if (bmi < 28) {
  console.log('超重,BMI:' + bmi.toFixed(2))
} else {
  console.log('肥胖,BMI:' + bmi.toFixed(2))
}

// 输出:正常,BMI:21.22
示例5:月份季节判断
javascript
// 根据月份判断季节
let month = 5

if (month >= 3 && month <= 5) {
  console.log('春季')
} else if (month >= 6 && month <= 8) {
  console.log('夏季')
} else if (month >= 9 && month <= 11) {
  console.log('秋季')
} else if (month === 12 || month === 1 || month === 2) {
  console.log('冬季')
} else {
  console.log('无效的月份')
}

// 输出:春季

2.2.3.3 执行流程
流程图:
┌─────────┐
│  开始    │
└────┬────┘


┌─────────┐
│ 条件1?   │
└────┬────┘

     ├─── true ───┐
     │            ▼
     │      ┌──────────┐
     │      │ 执行代码  │
     │      └────┬─────┘
     │           │
     │           ▼
     │      ┌──────────┐
     │      │ 跳出分支 │
     │      └────┬─────┘
     │           │
     │           │
     │           ▼
     │      ┌──────────┐
     │           │ 继续执行 │
     │           └────┬─────┘
     │                │
     │                │
     └── false ──────┤


               ┌─────────┐
               │ 条件2?   │
               └────┬────┘

                    ├─── true ───┐
                    │            ▼
                    │      ┌──────────┐
                    │      │ 执行代码  │
                    │      └────┬─────┘
                    │           │
                    │           ▼
                    │      ┌──────────┐
                    │      │ 跳出分支 │
                    │      └────┬─────┘
                    │           │
                    │           ▼
                    │      ┌──────────┐
                    │           │ 继续执行 │
                    │           └────┬─────┘
                    │                │
                    │                │
                    └── false ──────┤


                              ┌─────────┐
                              │  ...    │
                              │更多条件  │
                              └────┬────┘


                              ┌─────────┐
                              │  else   │
                              │执行代码 │
                              └────┬────┘


                              ┌──────────┐
                              │ 继续执行 │
                              └──────────┘

2.2.3.4 实际应用场景
场景1:用户等级判断
javascript
// 根据积分判断用户等级
let points = 1250

if (points < 100) {
  console.log('青铜会员')
} else if (points < 500) {
  console.log('白银会员')
} else if (points < 1000) {
  console.log('黄金会员')
} else if (points < 2000) {
  console.log('铂金会员')
} else {
  console.log('钻石会员')
}

// 输出:铂金会员
场景2:折扣计算
javascript
// 根据购买数量计算折扣
let quantity = 15

if (quantity < 5) {
  console.log('无折扣')
} else if (quantity < 10) {
  console.log('9折优惠')
} else if (quantity < 20) {
  console.log('8折优惠')
} else if (quantity < 50) {
  console.log('7折优惠')
} else {
  console.log('6折优惠')
}

// 输出:8折优惠
场景3:温度提示
javascript
// 根据温度给出提示
let temperature = -5

if (temperature < 0) {
  console.log('严寒,注意保暖')
} else if (temperature < 10) {
  console.log('寒冷')
} else if (temperature < 20) {
  console.log('凉爽')
} else if (temperature < 30) {
  console.log('温暖')
} else if (temperature < 35) {
  console.log('炎热')
} else {
  console.log('酷热,注意防暑')
}

// 输出:严寒,注意保暖
场景4:文件大小显示
javascript
// 根据文件大小选择合适的单位
let fileSize = 2048576  // 字节

if (fileSize < 1024) {
  console.log(fileSize + ' B')
} else if (fileSize < 1024 * 1024) {
  console.log((fileSize / 1024).toFixed(2) + ' KB')
} else if (fileSize < 1024 * 1024 * 1024) {
  console.log((fileSize / (1024 * 1024)).toFixed(2) + ' MB')
} else {
  console.log((fileSize / (1024 * 1024 * 1024)).toFixed(2) + ' GB')
}

// 输出:2.00 MB
场景5:交通信号灯
javascript
// 模拟交通信号灯控制
let light = 'yellow'

if (light === 'green') {
  console.log('绿灯,可以通行')
} else if (light === 'yellow') {
  console.log('黄灯,请减速停车')
} else if (light === 'red') {
  console.log('红灯,请停车等待')
} else {
  console.log('无效的信号灯')
}

// 输出:黄灯,请减速停车

2.2.3.5 多分支执行特性

重要特性:只执行第一个为 true 的条件

javascript
// 示例1:条件重叠的情况
let score = 85

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

// 输出:及格
// 解释:score >= 60 为 true,执行后直接跳过后面所有条件
// 注意:这不是正确的写法,应该从高到低判断

// 正确写法:从高到低
let score2 = 85

if (score2 >= 90) {
  console.log('优秀')
} else if (score2 >= 80) {
  console.log('良好')
} else if (score2 >= 70) {
  console.log('中等')
} else if (score2 >= 60) {
  console.log('及格')
}

// 输出:良好

条件判断的顺序很重要

javascript
// 示例2:不同的判断顺序
let age = 18

// 顺序1:从高到低
if (age >= 18) {
  console.log('成年人')
} else if (age >= 12) {
  console.log('青少年')
} else if (age >= 6) {
  console.log('儿童')
}

// 输出:成年人

// 顺序2:从低到高
if (age >= 6) {
  console.log('儿童')
} else if (age >= 12) {
  console.log('青少年')
} else if (age >= 18) {
  console.log('成年人')
}

// 输出:儿童(错误的顺序!)

2.2.3.6 多分支 vs 多个独立 if
javascript
// 多分支 if-else if-else:只执行一个分支
let x = 10

if (x > 5) {
  console.log('A')
} else if (x > 0) {
  console.log('B')
} else {
  console.log('C')
}
// 输出:A

// 多个独立 if:可能执行多个分支
let y = 10

if (y > 5) {
  console.log('A')
}
if (y > 0) {
  console.log('B')
}
if (y < 0) {
  console.log('C')
}
// 输出:
// A
// B

// 使用场景:
// - 多分支:多个条件互斥,只需要执行一个
// - 多个独立if:多个条件独立,可能需要执行多个

2.2.3.7 注意事项

重要注意事项

  1. 条件顺序很重要
javascript
// 推荐:从高到低或从特殊到一般
if (score >= 90) {
  console.log('优秀')
} else if (score >= 60) {
  console.log('及格')
}

// 不推荐:从低到高
if (score >= 60) {
  console.log('及格')  // 永远只会输出这个
} else if (score >= 90) {
  console.log('优秀')  // 永远不会执行
}
  1. 避免重复条件
javascript
// 不推荐:条件重复
if (age >= 18) {
  console.log('成年')
} else if (age >= 18) {  // 冗余
  console.log('已成年')
}

// 推荐:简化条件
if (age >= 18) {
  console.log('成年')
}
  1. 考虑所有可能的情况
javascript
// 推荐:使用 else 处理默认情况
if (day === 1) {
  console.log('周一')
} else if (day === 2) {
  console.log('周二')
} else if (day === 3) {
  console.log('周三')
} else if (day === 4) {
  console.log('周四')
} else if (day === 5) {
  console.log('周五')
} else if (day === 6) {
  console.log('周六')
} else if (day === 7) {
  console.log('周日')
} else {
  console.log('无效的日期')
}

// 不推荐:缺少默认情况
if (day === 1) {
  console.log('周一')
} else if (day === 2) {
  console.log('周二')
}
// 如果 day = 8,什么都不会输出
  1. 条件之间应该互斥
javascript
// 推荐:互斥条件
if (score >= 90) {
  console.log('优秀')
} else if (score >= 80) {
  console.log('良好')
}
// score >= 80 隐含了 score < 90

// 不推荐:条件不清晰
if (score >= 90) {
  console.log('优秀')
} else if (score < 90 && score >= 80) {
  console.log('良好')  // < 90 是多余的
}
  1. 代码块建议使用花括号
javascript
// 推荐
if (score >= 90) {
  console.log('优秀')
} else if (score >= 80) {
  console.log('良好')
} else {
  console.log('继续努力')
}

// 不推荐
if (score >= 90)
  console.log('优秀')
else if (score >= 80)
  console.log('良好')
else
  console.log('继续努力')

2.2.3.8 面试题
javascript
// 面试题1:以下代码输出什么?
let x = 10

if (x > 5) {
  console.log('A')
} else if (x > 8) {
  console.log('B')
} else if (x > 0) {
  console.log('C')
}
// 输出:A
// 解释:x > 5 为 true,执行后跳过后面所有条件

// 面试题2:以下代码输出什么?
let score = 85

if (score >= 90) {
  console.log('A')
} else if (score >= 80) {
  console.log('B')
} else if (score >= 70) {
  console.log('C')
} else if (score >= 60) {
  console.log('D')
} else {
  console.log('E')
}
// 输出:B
// 解释:85 >= 90 为 false,85 >= 80 为 true

// 面试题3:以下代码输出什么?
let y = 5

if (y > 10) {
  console.log('A')
} else if (y > 5) {
  console.log('B')
} else if (y === 5) {
  console.log('C')
} else {
  console.log('D')
}
// 输出:C
// 解释:y > 10 为 false,y > 5 为 false,y === 5 为 true

// 面试题4:以下代码输出什么?
let age = 18

if (age >= 6) {
  console.log('儿童')
} else if (age >= 12) {
  console.log('青少年')
} else if (age >= 18) {
  console.log('成年人')
}
// 输出:儿童
// 解释:age >= 6 为 true,执行后跳过后面
// 这就是为什么条件顺序很重要!

// 面试题5:以下代码输出什么?
let num = 0

if (num > 0) {
  console.log('正数')
} else if (num < 0) {
  console.log('负数')
} else {
  console.log('零')
}
// 输出:零
// 解释:0 既不是正数也不是负数

// 面试题6:以下代码输出什么?
let day = 8

if (day === 1) {
  console.log('周一')
} else if (day === 2) {
  console.log('周二')
} else if (day === 3) {
  console.log('周三')
} else if (day === 4) {
  console.log('周四')
} else if (day === 5) {
  console.log('周五')
} else if (day === 6) {
  console.log('周六')
} else if (day === 7) {
  console.log('周日')
}
// 无输出
// 解释:没有 else,day = 8 不满足任何条件

// 面试题7:以下代码输出什么?
let a = 10

if (a > 5) {
  console.log('1')
}
if (a > 0) {
  console.log('2')
}
if (a > 15) {
  console.log('3')
}
// 输出:
// 1
// 2
// 解释:这是三个独立的 if,不是多分支

// 面试题8:以下代码输出什么?
let str = 'hello'

if (str === 'hello') {
  console.log('A')
} else if (str === 'world') {
  console.log('B')
} else if (typeof str === 'string') {
  console.log('C')
}
// 输出:A
// 解释:第一个条件就满足了

2.2.3.9 最佳实践

推荐做法

  1. 条件从特殊到一般,从高到低
javascript
// 推荐:从高到低判断成绩
if (score >= 90) {
  console.log('优秀')
} else if (score >= 80) {
  console.log('良好')
} else if (score >= 70) {
  console.log('中等')
} else if (score >= 60) {
  console.log('及格')
} else {
  console.log('不及格')
}
  1. 使用 else 处理默认情况
javascript
// 推荐:完整的条件覆盖
if (day === 1) {
  console.log('周一')
} else if (day === 2) {
  console.log('周二')
} else if (day === 3) {
  console.log('周三')
} else if (day === 4) {
  console.log('周四')
} else if (day === 5) {
  console.log('周五')
} else if (day === 6) {
  console.log('周六')
} else if (day === 7) {
  console.log('周日')
} else {
  console.log('无效的日期')
}
  1. 将复杂条件提取为变量
javascript
// 推荐:提高可读性
let isVIP = points > 1000
let isRegular = points > 100
let isNewUser = points === 0

if (isVIP) {
  console.log('VIP用户')
} else if (isRegular) {
  console.log('普通用户')
} else if (isNewUser) {
  console.log('新用户')
} else {
  console.log('其他')
}
  1. 使用严格相等
javascript
// 推荐
if (day === 1) {
  console.log('周一')
} else if (day === 2) {
  console.log('周二')
}

// 不推荐
if (day == 1) {
  console.log('周一')
}
  1. 考虑使用 switch 语句
javascript
// 如果条件是同一个变量的不同值,考虑使用 switch
let day = 1

// 使用 if-else if
if (day === 1) {
  console.log('周一')
} else if (day === 2) {
  console.log('周二')
} else if (day === 3) {
  console.log('周三')
} else if (day === 4) {
  console.log('周四')
} else if (day === 5) {
  console.log('周五')
} else if (day === 6) {
  console.log('周六')
} else if (day === 7) {
  console.log('周日')
}

// 使用 switch(更简洁)
switch (day) {
  case 1:
    console.log('周一')
    break
  case 2:
    console.log('周二')
    break
  case 3:
  case 4:
  case 5:
    console.log('工作日')
    break
  case 6:
  case 7:
    console.log('周末')
    break
  default:
    console.log('无效')
}

避免做法

  1. 避免过多的 else if
javascript
// 不推荐:条件过多
if (level === 1) {
  console.log('等级1')
} else if (level === 2) {
  console.log('等级2')
} else if (level === 3) {
  console.log('等级3')
} else if (level === 4) {
  console.log('等级4')
} else if (level === 5) {
  console.log('等级5')
} else if (level === 6) {
  console.log('等级6')
} else if (level === 7) {
  console.log('等级7')
} else if (level === 8) {
  console.log('等级8')
} else if (level === 9) {
  console.log('等级9')
} else {
  console.log('等级10')
}

// 推荐:使用对象或 switch
const levels = {
  1: '等级1',
  2: '等级2',
  3: '等级3',
  4: '等级4',
  5: '等级5',
  6: '等级6',
  7: '等级7',
  8: '等级8',
  9: '等级9',
  10: '等级10'
}
console.log(levels[level] || '无效等级')
  1. 避免条件顺序错误
javascript
// 不推荐:顺序错误
if (age >= 6) {
  console.log('儿童')
} else if (age >= 12) {
  console.log('青少年')
} else if (age >= 18) {
  console.log('成年人')
}

// 推荐:正确顺序
if (age >= 18) {
  console.log('成年人')
} else if (age >= 12) {
  console.log('青少年')
} else if (age >= 6) {
  console.log('儿童')
}
  1. 避免嵌套过深
javascript
// 不推荐:嵌套过深
if (condition1) {
  if (condition2) {
    if (condition3) {
      console.log('A')
    } else {
      console.log('B')
    }
  } else {
    console.log('C')
  }
} else {
  console.log('D')
}

// 推荐:扁平化
if (condition1 && condition2 && condition3) {
  console.log('A')
} else if (condition1 && condition2) {
  console.log('B')
} else if (condition1) {
  console.log('C')
} else {
  console.log('D')
}

// 或使用提前返回
if (!condition1) {
  console.log('D')
  return
}
if (!condition2) {
  console.log('C')
  return
}
if (!condition3) {
  console.log('B')
  return
}
console.log('A')

2.2.3.10 总结

多分支语句要点

  1. 语法if (条件1) { } else if (条件2) { } else { }
  2. 执行:从上到下依次判断,执行第一个为 true 的条件对应的代码块
  3. 注意:条件顺序很重要,建议从特殊到一般、从高到低
  4. 推荐:使用 else 处理默认情况,将复杂条件提取为变量
  5. 最佳实践:避免过多的 else if,条件互斥,考虑使用 switch 语句
  6. 关键特性:只执行一个分支,执行后跳过剩余所有条件

2.2.4 三元运算符

三元运算符(条件运算符)是 JavaScript 中唯一需要三个操作数的运算符,它是 if...else 语句的简洁写法。

2.2.4.1 基本语法
javascript
条件 ? 表达式1 : 表达式2

语法说明

  • 条件:一个表达式,会被转换为布尔值
  • ?:问号,分隔条件和结果
  • 表达式1:条件为 true 时执行的表达式
  • ::冒号,分隔两个结果
  • 表达式2:条件为 false 时执行的表达式
  • 三元运算符是表达式,会返回一个值

2.2.4.2 代码示例
示例1:基础用法
javascript
// 判断成年
let age = 18
let result = age >= 18 ? '成年' : '未成年'
console.log(result)  // 成年

// 判断奇偶数
let number = 7
let evenOrOdd = number % 2 === 0 ? '偶数' : '奇数'
console.log(evenOrOdd)  // 奇数

// 判断正负数
let num = -5
let positiveOrNegative = num > 0 ? '正数' : num < 0 ? '负数' : '零'
console.log(positiveOrNegative)  // 负数
示例2:成绩判断
javascript
// 简单判断及格
let score = 85
let level = score >= 60 ? '及格' : '不及格'
console.log(level)  // 及格

// 成绩等级
let score2 = 85
let grade = score2 >= 90 ? '优秀' : score2 >= 80 ? '良好' : score2 >= 70 ? '中等' : score2 >= 60 ? '及格' : '不及格'
console.log(grade)  // 良好
示例3:默认值设置
javascript
// 设置默认值
let name = ''
let displayName = name ? name : '匿名用户'
console.log(displayName)  // 匿名用户

let name2 = '小明'
let displayName2 = name2 ? name2 : '匿名用户'
console.log(displayName2)  // 小明

// 更简洁的写法(使用 ||)
let name3 = ''
let displayName3 = name3 || '匿名用户'
console.log(displayName3)  // 匿名用户
示例4:计算折扣
javascript
// 计算折扣
let isMember = true
let price = 100
let finalPrice = isMember ? price * 0.8 : price
console.log(finalPrice)  // 80

// 非会员情况
let isMember2 = false
let price2 = 100
let finalPrice2 = isMember2 ? price2 * 0.8 : price2
console.log(finalPrice2)  // 100
示例5:函数参数默认值
javascript
// 使用三元运算符设置默认值
function greet(name) {
  name = name ? name : '朋友'
  console.log('你好,' + name)
}

greet()        // 输出:你好,朋友
greet('小明')  // 输出:你好,小明

// 使用 || 更简洁
function greet2(name) {
  name = name || '朋友'
  console.log('你好,' + name)
}

greet2()        // 输出:你好,朋友
greet2('小红')  // 输出:你好,小红

2.2.4.3 执行流程
流程图:
┌─────────┐
│  开始    │
└────┬────┘


┌─────────┐
│ 判断条件 │
└────┬────┘

     ├─── true ───┐
     │            ▼
     │      ┌──────────┐
     │      │ 执行     │
     │      │ 表达式1  │
     │      └────┬─────┘
     │           │
     │           ▼
     │      ┌──────────┐
     │      │ 返回结果1 │
     │      └────┬─────┘
     │           │
     │           │
     │           ▼
     │      ┌──────────┐
     │           │ 继续执行 │
     │           └────┬─────┘
     │                │
     │                │
     └── false ────┤


               ┌──────────┐
               │ 执行     │
               │ 表达式2  │
               └────┬─────┘


               ┌──────────┐
               │ 返回结果2 │
               └────┬─────┘


               ┌──────────┐
               │ 继续执行 │
               └──────────┘

2.2.4.4 三元运算符 vs if 语句
javascript
// 三元运算符(表达式)
let age = 20
let level = age >= 18 ? '成年' : '未成年'
console.log(level)  // 成年

// if 语句(语句)
let age2 = 20
let level2
if (age2 >= 18) {
  level2 = '成年'
} else {
  level2 = '未成年'
}
console.log(level2)  // 成年

// 三元运算符可以直接用于赋值
let result = age >= 18 ? '成年' : '未成年'

// if 语句不能直接用于赋值
// let result2 = if (age >= 18) { ... }  // 报错

// 三元运算符可以嵌套在表达式中
let message = `你今年${age}岁,${age >= 18 ? '是' : '不是'}成年人`
console.log(message)  // 你今年20岁,是成年人

// if 语句不能嵌套在表达式中
// let message2 = `你今年${age}岁,${if (age >= 18) '是'}成年人`  // 报错

使用场景对比

|| 特性 | 三元运算符 | if 语句 | ||------|-----------|---------| || 类型 | 表达式 | 语句 | || 返回值 | 有返回值 | 无返回值 | || 嵌套 | 可以嵌套在表达式中 | 不能嵌套在表达式中 | || 可读性 | 简单场景可读性好 | 复杂逻辑更清晰 | || 适用场景 | 简单的条件赋值 | 复杂的条件逻辑 | || 代码长度 | 更简洁 | 较长 |


2.2.4.5 嵌套三元运算符
javascript
// 嵌套三元运算符
let score = 85
let level = score >= 90 ? '优秀' : score >= 80 ? '良好' : score >= 70 ? '中等' : score >= 60 ? '及格' : '不及格'
console.log(level)  // 良好

// 等价的 if 语句
let score2 = 85
let level2
if (score2 >= 90) {
  level2 = '优秀'
} else if (score2 >= 80) {
  level2 = '良好'
} else if (score2 >= 70) {
  level2 = '中等'
} else if (score2 >= 60) {
  level2 = '及格'
} else {
  level2 = '不及格'
}
console.log(level2)  // 良好

// 嵌套结构解析
// score >= 90 ? '优秀' : (score >= 80 ? '良好' : (score >= 70 ? '中等' : (score >= 60 ? '及格' : '不及格')))
//                   └────────────────────────────────────────────────────────────────────────────────────┘
//                                      嵌套的三元表达式

注意:嵌套过深会影响可读性,建议不超过 2 层嵌套。


2.2.4.6 实际应用场景
场景1:表单默认值
javascript
// 设置表单字段的默认值
let username = ''
let defaultValue = username ? username : '请输入用户名'
console.log(defaultValue)  // 请输入用户名

let password = '123456'
let defaultPassword = password ? password : '请输入密码'
console.log(defaultPassword)  // 123456
场景2:动态类名
javascript
// 根据状态设置类名
let isActive = true
let className = isActive ? 'active' : 'inactive'
console.log(className)  // active

let isDisabled = false
let disabledClass = isDisabled ? 'disabled' : 'enabled'
console.log(disabledClass)  // enabled
场景3:条件渲染
javascript
// 根据条件显示不同的内容
let isLoggedIn = true
let welcomeMessage = isLoggedIn ? '欢迎回来!' : '请先登录'
console.log(welcomeMessage)  // 欢迎回来!

let hasPermission = false
let permissionMessage = hasPermission ? '有权限访问' : '没有权限访问'
console.log(permissionMessage)  // 没有权限访问
场景4:数组/对象操作
javascript
// 根据条件选择不同的数组
let type = 'admin'
let menu = type === 'admin' ? ['首页', '管理', '设置'] : ['首页', '个人中心']
console.log(menu)  // ['首页', '管理', '设置']

// 根据条件选择不同的对象
let theme = 'dark'
let themeConfig = theme === 'dark' ? { bg: '#000', color: '#fff' } : { bg: '#fff', color: '#000' }
console.log(themeConfig)  // { bg: '#000', color: '#fff' }
场景5:模板字符串中使用
javascript
// 在模板字符串中使用三元运算符
let age = 20
let message = `你今年${age}岁,${age >= 18 ? '是' : '不是'}成年人`
console.log(message)  // 你今年20岁,是成年人

// 另一个例子
let score = 85
let result = `你的成绩是${score}分,评级为${score >= 90 ? '优秀' : score >= 80 ? '良好' : '及格'}`
console.log(result)  // 你的成绩是85分,评级为良好

2.2.4.7 高级用法
多条件判断
javascript
// 多条件判断
let day = 1
let dayName = day === 1 ? '周一' : day === 2 ? '周二' : day === 3 ? '周三' : day === 4 ? '周四' : day === 5 ? '周五' : day === 6 ? '周六' : day === 7 ? '周日' : '无效'
console.log(dayName)  // 周一

// 更清晰的写法:使用对象映射
const dayMap = {
  1: '周一',
  2: '周二',
  3: '周三',
  4: '周四',
  5: '周五',
  6: '周六',
  7: '周日'
}
let day2 = 1
let dayName2 = dayMap[day2] || '无效'
console.log(dayName2)  // 周一
复杂逻辑简化
javascript
// 复杂逻辑
let user = {
  name: '小明',
  role: 'admin',
  isActive: true
}

let canEdit = user.isActive && (user.role === 'admin' || user.role === 'editor') ? '可以编辑' : '不能编辑'
console.log(canEdit)  // 可以编辑

// 提取条件,提高可读性
let isActive = user.isActive
let hasEditPermission = user.role === 'admin' || user.role === 'editor'
let canEdit2 = isActive && hasEditPermission ? '可以编辑' : '不能编辑'
console.log(canEdit2)  // 可以编辑
链式三元运算
javascript
// 链式三元运算
let num = 0
let result = num > 0 ? '正数' : num < 0 ? '负数' : '零'
console.log(result)  // 零

// 另一个例子
let age = 25
let stage = age < 6 ? '儿童' : age < 12 ? '少年' : age < 18 ? '青少年' : age < 30 ? '青年' : age < 50 ? '中年' : '老年'
console.log(stage)  // 青年

2.2.4.8 注意事项

重要注意事项

  1. 三元运算符是表达式
javascript
// 正确:可以用于赋值
let result = age >= 18 ? '成年' : '未成年'

// 错误:不能单独作为语句使用
// age >= 18 ? '成年' : '未成年'  // 不推荐
// 应该:let x = age >= 18 ? '成年' : '未成年'
  1. 避免过度嵌套
javascript
// 不推荐:嵌套过深,可读性差
let level = score >= 90 ? '优秀' : score >= 80 ? '良好' : score >= 70 ? '中等' : score >= 60 ? '及格' : '不及格'

// 推荐:使用 if 语句
let level
if (score >= 90) {
  level = '优秀'
} else if (score >= 80) {
  level = '良好'
} else if (score >= 70) {
  level = '中等'
} else if (score >= 60) {
  level = '及格'
} else {
  level = '不及格'
}
  1. 表达式1 和表达式2 的类型
javascript
// 注意:两个表达式应该返回相同类型的值
// 不推荐:返回不同类型
let result = age >= 18 ? '成年' : false

// 推荐:返回相同类型
let result = age >= 18 ? '成年' : '未成年'
  1. 优先级问题
javascript
// 注意优先级
let a = 10
// 错误:优先级不明确
// let result = a > 5 ? 1 + 2 : 3 + 4  // 可能有问题

// 推荐:使用括号明确优先级
let result = (a > 5) ? (1 + 2) : (3 + 4)
console.log(result)  // 3
  1. 空值处理
javascript
// 注意:null 和 undefined 的处理
let name = null
let displayName = name ? name : '匿名用户'
console.log(displayName)  // 匿名用户

let age = undefined
let ageDisplay = age ? age : '年龄未知'
console.log(ageDisplay)  // 年龄未知

2.2.4.9 面试题
javascript
// 面试题1:以下代码输出什么?
let age = 20
let result = age >= 18 ? '成年' : '未成年'
console.log(result)  // 成年
// 解释:age >= 18 为 true,返回 '成年'

// 面试题2:以下代码输出什么?
let score = 85
let level = score >= 90 ? '优秀' : score >= 80 ? '良好' : score >= 70 ? '中等' : score >= 60 ? '及格' : '不及格'
console.log(level)  // 良好
// 解释:85 >= 90 为 false,85 >= 80 为 true,返回 '良好'

// 面试题3:以下代码输出什么?
let num = 0
let result = num > 0 ? '正数' : num < 0 ? '负数' : '零'
console.log(result)  // 零
// 解释:0 > 0 为 false,0 < 0 为 false,返回 '零'

// 面试题4:以下代码输出什么?
let str = ''
let result = str ? str : '空字符串'
console.log(result)  // 空字符串
// 解释:空字符串是假值,返回 '空字符串'

// 面试题5:以下代码输出什么?
let arr = []
let result = arr.length ? '非空' : '空'
console.log(result)  // 空
// 解释:空数组的长度是 0(假值),返回 '空'

// 面试题6:以下代码输出什么?
let obj = {}
let result = Object.keys(obj).length ? '非空' : '空'
console.log(result)  // 空
// 解释:空对象的 keys 数组长度为 0,返回 '空'

// 面试题7:以下代码输出什么?
let a = 10
let b = 20
let result = a > b ? 'A' : a < b ? 'B' : 'C'
console.log(result)  // B
// 解释:10 > 20 为 false,10 < 20 为 true,返回 'B'

// 面试题8:以下代码输出什么?
let x = 5
let y = x > 0 ? 'positive' : x < 0 ? 'negative' : 'zero'
console.log(y)  // positive
// 解释:5 > 0 为 true,返回 'positive'

// 面试题9:以下代码输出什么?
let day = 1
let dayName = day === 1 ? '周一' : day === 2 ? '周二' : day === 3 ? '周三' : '周四'
console.log(dayName)  // 周一
// 解释:day === 1 为 true,返回 '周一'

// 面试题10:以下代码输出什么?
let isMember = true
let hasCoupon = false
let discount = isMember ? 0.8 : hasCoupon ? 0.9 : 1
console.log(discount)  // 0.8
// 解释:isMember 为 true,返回 0.8

// 面试题11:以下代码输出什么?
let age = 18
let type = age < 6 ? '儿童' : age < 12 ? '少年' : age < 18 ? '青少年' : '成年'
console.log(type)  // 成年
// 解释:age < 6、< 12、< 18 都为 false,返回 '成年'

// 面试题12:以下代码输出什么?
let score = 60
let result = score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 60 ? 'D' : 'E'
console.log(result)  // D
// 解释:60 >= 90、>= 80、>= 70 都为 false,60 >= 60 为 true,返回 'D'

2.2.4.10 最佳实践

推荐做法

  1. 简单条件使用三元运算符
javascript
// 推荐:简单的二元选择
let result = age >= 18 ? '成年' : '未成年'
  1. 使用括号提高可读性
javascript
// 推荐
let result = (score >= 60) ? '及格' : '不及格'
  1. 复杂逻辑使用 if 语句
javascript
// 推荐:复杂逻辑使用 if
let level
if (score >= 90) {
  level = '优秀'
} else if (score >= 80) {
  level = '良好'
} else if (score >= 70) {
  level = '中等'
} else if (score >= 60) {
  level = '及格'
} else {
  level = '不及格'
}
  1. 相同类型的返回值
javascript
// 推荐
let result = age >= 18 ? '成年' : '未成年'
  1. 提取复杂条件
javascript
// 推荐
let isAdult = age >= 18
let hasPermission = role === 'admin' || role === 'editor'
let canAccess = isAdult && hasPermission ? '可以访问' : '不能访问'
  1. 使用对象映射替代嵌套
javascript
// 推荐
const dayMap = {
  1: '周一',
  2: '周二',
  3: '周三',
  4: '周四',
  5: '周五',
  6: '周六',
  7: '周日'
}
let dayName = dayMap[day] || '无效'

// 不推荐:嵌套三元
let dayName = day === 1 ? '周一' : day === 2 ? '周二' : day === 3 ? '周三' : '无效'

避免做法

  1. 避免过度嵌套
javascript
// 不推荐
let level = score >= 90 ? '优秀' : score >= 80 ? '良好' : score >= 70 ? '中等' : score >= 60 ? '及格' : '不及格'
  1. 避免在不同类型之间切换
javascript
// 不推荐:返回值类型不一致
let result = age >= 18 ? '成年' : false

// 推荐
let result = age >= 18 ? '成年' : '未成年'
  1. 避免在复杂逻辑中使用三元
javascript
// 不推荐
let result = condition1 && condition2 ? option1 : condition3 || condition4 ? option2 : option3

// 推荐:使用 if 语句
let result
if (condition1 && condition2) {
  result = option1
} else if (condition3 || condition4) {
  result = option2
} else {
  result = option3
}
  1. 避免三元运算符单独作为语句
javascript
// 不推荐
// age >= 18 ? console.log('成年') : console.log('未成年')

// 推荐
if (age >= 18) {
  console.log('成年')
} else {
  console.log('未成年')
}

2.2.4.11 三元运算符与 switch 对比
javascript
// 使用三元运算符(适合简单的值判断)
let day = 1
let dayName = day === 1 ? '周一' : day === 2 ? '周二' : day === 3 ? '周三' : '其他'

// 使用 switch(适合多个值的判断)
let day = 1
let dayName
switch (day) {
  case 1:
    dayName = '周一'
    break
  case 2:
    dayName = '周二'
    break
  case 3:
    dayName = '周三'
    break
  default:
    dayName = '其他'
}

// 使用对象映射(最简洁)
const dayMap = {
  1: '周一',
  2: '周二',
  3: '周三'
}
let dayName = dayMap[day] || '其他'

对比表格

|| 特性 | 三元运算符 | switch 语句 | 对象映射 | ||------|-----------|-------------|---------| || 简洁性 | 中等 | 较长 | 最简洁 | || 可读性 | 简单场景好 | 清晰 | 非常清晰 | || 性能 | 快 | 快 | 最快 | || 适用场景 | 简单二元选择 | 多值判断 | 已知键值对 | || 灵活性 | 高 | 中 | 中 | || 默认值 | 较复杂 | 简单(default) | 简单(||) |


2.2.4.12 总结

三元运算符要点

  1. 语法条件 ? 表达式1 : 表达式2
  2. 特性:是表达式,会返回值
  3. 执行:条件为 true 返回表达式1,为 false 返回表达式2
  4. 优势:简洁,可以嵌套在表达式中
  5. 注意:避免过度嵌套,复杂逻辑使用 if 语句
  6. 最佳实践:简单条件使用三元,复杂条件使用 if,多值判断使用对象映射
  7. 优先级:注意运算符优先级,必要时使用括号

2.2.5 switch 语句

switch 语句用于基于不同的条件来执行不同的代码块。它特别适合处理一个变量的多个可能值的情况。

2.2.5.1 基本语法
javascript
switch (表达式) {
  case 值1:
    // 当表达式的值等于值1时执行的代码
    break
  case 值2:
    // 当表达式的值等于值2时执行的代码
    break
  case 值3:
    // 当表达式的值等于值3时执行的代码
    break
  default:
    // 当表达式的值不等于任何case时执行的代码(可选)
}

语法说明

  • switch:关键字,开始 switch 语句
  • 表达式:要判断的表达式
  • case:关键字,表示一个可能的情况
  • :case 后面的值(可以是数字、字符串等)
  • break:关键字,跳出 switch 语句(可选,但推荐使用)
  • default:默认情况(可选)
  • 执行顺序:从上到下依次匹配,执行第一个匹配的 case 代码块

2.2.5.2 代码示例
示例1:基础用法
javascript
// 判断星期几
let day = 3

switch (day) {
  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
  case 7:
    console.log('周日')
    break
}

// 输出:周三

// 字符串值的情况
let fruit = 'apple'

switch (fruit) {
  case 'apple':
    console.log('苹果')
    break
  case 'banana':
    console.log('香蕉')
    break
  case 'orange':
    console.log('橙子')
    break
}

// 输出:苹果
示例2:使用 default
javascript
// 使用 default 处理默认情况
let day = 8

switch (day) {
  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
  case 7:
    console.log('周日')
    break
  default:
    console.log('无效的日期')
}

// 输出:无效的日期
示例3:成绩等级判断
javascript
// 判断成绩等级
let score = 85
let level

switch (true) {
  case score >= 90:
    level = '优秀'
    break
  case score >= 80:
    level = '良好'
    break
  case score >= 70:
    level = '中等'
    break
  case score >= 60:
    level = '及格'
    break
  default:
    level = '不及格'
}

console.log(level)  // 良好

// 另一种方式:先计算等级
let score2 = 95
let grade = Math.floor(score2 / 10)

switch (grade) {
  case 10:
  case 9:
    console.log('优秀')
    break
  case 8:
    console.log('良好')
    break
  case 7:
    console.log('中等')
    break
  case 6:
    console.log('及格')
    break
  default:
    console.log('不及格')
}

// 输出:优秀
示例4:月份季节判断
javascript
// 根据月份判断季节
let month = 5

switch (month) {
  case 3:
  case 4:
  case 5:
    console.log('春季')
    break
  case 6:
  case 7:
  case 8:
    console.log('夏季')
    break
  case 9:
  case 10:
  case 11:
    console.log('秋季')
    break
  case 12:
  case 1:
  case 2:
    console.log('冬季')
    break
  default:
    console.log('无效的月份')
}

// 输出:春季
示例5:计算器
javascript
// 简单计算器
let operator = '+'
let a = 10
let b = 5
let result

switch (operator) {
  case '+':
    result = a + b
    break
  case '-':
    result = a - b
    break
  case '*':
    result = a * b
    break
  case '/':
    result = a / b
    break
  case '%':
    result = a % b
    break
  default:
    console.log('无效的运算符')
}

console.log(result)  // 15

// 另一个例子
let operator2 = '*'
switch (operator2) {
  case '+':
    result = a + b
    break
  case '-':
    result = a - b
    break
  case '*':
    result = a * b
    break
  case '/':
    result = a / b
    break
  default:
    console.log('无效的运算符')
}

console.log(result)  // 50

2.2.5.3 执行流程
流程图:
┌─────────┐
│  开始    │
└────┬────┘


┌─────────┐
│ 计算表达式│
│   的值   │
└────┬────┘


┌─────────┐
│ case 值1 │
│ 匹配?   │
└────┬────┘

     ├─── 匹配 ───┐
     │            ▼
     │      ┌──────────┐
     │      │ 执行代码  │
     │      └────┬─────┘
     │           │
     │           ▼
     │      ┌──────────┐
     │      │ 遇到break? │
     │      └────┬─────┘
     │           │
     │     ┌─────┴─────┐
     │     │           │
     │   是│          否│
     │     │           │
     │     ▼           ▼
     │ ┌──────┐    ┌──────────┐
     │ │跳出  │    │ 继续下个 │
     │ │switch│    │  case   │
     │ └──┬───┘    └────┬─────┘
     │    │             │
     │    │             │
     │    │             ▼
     │    │      ┌──────────┐
     │    │      │ case 值2 │
     │    │      │ 匹配?   │
     │    │      └────┬─────┘
     │    │           │
     │    │      ......(继续匹配)
     │    │           │
     │    │           ▼
     │    │      ┌──────────┐
     │    │      │ default  │
     │    │      │ 执行代码 │
     │    │      └────┬─────┘
     │    │           │
     │    │           ▼
     │    │      ┌──────────┐
     │    └─────┤ 继续执行 │
     │          └──────────┘

     └── 不匹配 ──┤


              ┌──────────┐
              │ case 值2 │
              │ 匹配?   │
              └────┬─────┘


              ......(继续匹配)

2.2.5.4 break 语句的重要性

break 的作用:跳出 switch 语句,防止代码继续向下执行。

javascript
// 示例1:使用 break
let day = 2

switch (day) {
  case 1:
    console.log('周一')
    break
  case 2:
    console.log('周二')
    break
  case 3:
    console.log('周三')
    break
}
// 输出:周二

// 示例2:不使用 break(case 穿透)
let day2 = 2

switch (day2) {
  case 1:
    console.log('周一')
  case 2:
    console.log('周二')
  case 3:
    console.log('周三')
}
// 输出:
// 周二
// 周三
// 解释:case 2 匹配后,因为没有 break,继续执行 case 3 的代码

// 示例3:有意利用 case 穿透
let month = 3

switch (month) {
  case 3:
  case 4:
  case 5:
    console.log('春季')
    break
  case 6:
  case 7:
  case 8:
    console.log('夏季')
    break
}
// 输出:春季
// 解释:month = 3 匹配 case 3,利用穿透继续执行,直到遇到 break

2.2.5.5 实际应用场景
场景1:用户角色权限
javascript
// 根据用户角色显示不同的菜单
let role = 'admin'

switch (role) {
  case 'admin':
    console.log('用户管理')
    console.log('系统设置')
  case 'editor':
    console.log('文章编辑')
    console.log('内容审核')
  case 'viewer':
    console.log('浏览内容')
    break
  default:
    console.log('访客')
}

// 输出:
// 用户管理
// 系统设置
// 文章编辑
// 内容审核
// 浏览内容
场景2:交通信号灯
javascript
// 模拟交通信号灯控制
let light = 'green'

switch (light) {
  case 'green':
    console.log('绿灯,可以通行')
    break
  case 'yellow':
    console.log('黄灯,请减速停车')
    break
  case 'red':
    console.log('红灯,请停车等待')
    break
  default:
    console.log('无效的信号灯')
}

// 输出:绿灯,可以通行
场景3:网络状态
javascript
// 根据网络状态显示提示
let networkStatus = 'offline'

switch (networkStatus) {
  case 'online':
    console.log('网络已连接')
    break
  case 'offline':
    console.log('网络已断开')
    break
  case 'loading':
    console.log('正在连接...')
    break
  default:
    console.log('未知状态')
}

// 输出:网络已断开
场景4:游戏状态
javascript
// 游戏状态管理
let gameState = 'playing'

switch (gameState) {
  case 'menu':
    console.log('显示主菜单')
    break
  case 'playing':
    console.log('游戏进行中')
    break
  case 'paused':
    console.log('游戏暂停')
    break
  case 'gameover':
    console.log('游戏结束')
    break
  default:
    console.log('未知状态')
}

// 输出:游戏进行中
场景5:HTTP 状态码
javascript
// 根据状态码返回不同的提示
let statusCode = 404

switch (statusCode) {
  case 200:
    console.log('成功')
    break
  case 201:
    console.log('创建成功')
    break
  case 400:
    console.log('请求错误')
    break
  case 401:
    console.log('未授权')
    break
  case 403:
    console.log('禁止访问')
    break
  case 404:
    console.log('未找到资源')
    break
  case 500:
    console.log('服务器错误')
    break
  default:
    console.log('未知状态码')
}

// 输出:未找到资源

2.2.5.6 switch vs if-else vs 三元运算符
javascript
// 场景:判断星期几
let day = 3

// 使用 switch
switch (day) {
  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
  case 7:
    console.log('周日')
    break
}

// 使用 if-else
if (day === 1) {
  console.log('周一')
} else if (day === 2) {
  console.log('周二')
} else if (day === 3) {
  console.log('周三')
} else if (day === 4) {
  console.log('周四')
} else if (day === 5) {
  console.log('周五')
} else if (day === 6) {
  console.log('周六')
} else if (day === 7) {
  console.log('周日')
}

// 使用对象映射(最简洁)
const dayMap = {
  1: '周一',
  2: '周二',
  3: '周三',
  4: '周四',
  5: '周五',
  6: '周六',
  7: '周日'
}
console.log(dayMap[day])

// 场景:简单的二元判断
let age = 18

// 使用三元运算符(最简洁)
let level = age >= 18 ? '成年' : '未成年'
console.log(level)

// 使用 if-else
if (age >= 18) {
  console.log('成年')
} else {
  console.log('未成年')
}

// 使用 switch(不推荐用于这种情况)
switch (true) {
  case age >= 18:
    console.log('成年')
    break
  default:
    console.log('未成年')
}

对比表格

|| 特性 | switch | if-else | 三元运算符 | ||------|--------|---------|-----------| || 适用场景 | 同一变量的多个值 | 复杂条件判断 | 简单二元选择 | || 可读性 | 多值判断时清晰 | 复杂逻辑清晰 | 简单场景清晰 | || 代码长度 | 较长 | 较长 | 最短 | || 灵活性 | 中等 | 最高 | 较低 | || 性能 | 快 | 快 | 快 | || 嵌套 | 不支持嵌套 | 支持嵌套 | 支持嵌套 |


2.2.5.7 switch 的高级用法
多个 case 共用同一代码
javascript
// 多个月份对应同一季节
let month = 3

switch (month) {
  case 3:
  case 4:
  case 5:
    console.log('春季')
    break
  case 6:
  case 7:
  case 8:
    console.log('夏季')
    break
  case 9:
  case 10:
  case 11:
    console.log('秋季')
    break
  case 12:
  case 1:
  case 2:
    console.log('冬季')
    break
  default:
    console.log('无效的月份')
}

// 输出:春季
表达式作为 case 值
javascript
// 使用表达式
let num = 10

switch (true) {
  case num > 0:
    console.log('正数')
    break
  case num < 0:
    console.log('负数')
    break
  default:
    console.log('零')
}

// 输出:正数

// 另一个例子
let score = 85

switch (true) {
  case score >= 90:
    console.log('优秀')
    break
  case score >= 80:
    console.log('良好')
    break
  case score >= 70:
    console.log('中等')
    break
  case score >= 60:
    console.log('及格')
    break
  default:
    console.log('不及格')
}

// 输出:良好
字符串匹配
javascript
// 字符串匹配
let fruit = 'APPLE'

switch (fruit.toLowerCase()) {
  case 'apple':
    console.log('苹果')
    break
  case 'banana':
    console.log('香蕉')
    break
  case 'orange':
    console.log('橙子')
    break
  default:
    console.log('未知水果')
}

// 输出:苹果

2.2.5.8 注意事项

重要注意事项

  1. 记得使用 break
javascript
// 推荐:每个 case 都使用 break
switch (day) {
  case 1:
    console.log('周一')
    break
  case 2:
    console.log('周二')
    break
}

// 不推荐:忘记 break(除非是有意的 case 穿透)
switch (day) {
  case 1:
    console.log('周一')
    // 忘记 break
  case 2:
    console.log('周二')
    // 忘记 break
}
  1. case 的值是严格相等
javascript
// switch 使用严格相等 === 比较
let x = '10'

switch (x) {
  case 10:
    console.log('A')  // 不会执行
    break
  case '10':
    console.log('B')  // 会执行
    break
}

// 输出:B
// 解释:'10' === 10 为 false,'10' === '10' 为 true
  1. default 可选
javascript
// 不使用 default
switch (day) {
  case 1:
    console.log('周一')
    break
  case 2:
    console.log('周二')
    break
}
// 如果 day = 3,什么都不会执行

// 使用 default
switch (day) {
  case 1:
    console.log('周一')
    break
  case 2:
    console.log('周二')
    break
  default:
    console.log('其他')
}
  1. case 值不能重复
javascript
// 错误:case 值重复
switch (day) {
  case 1:
    console.log('周一')
    break
  case 1:  // 重复,会报错
    console.log('一月')
    break
}
  1. switch 表达式可以是任何类型
javascript
// 数字
switch (num) { }

// 字符串
switch (str) { }

// 布尔值
switch (bool) { }

// 对象(引用比较)
switch (obj) { }  // 注意:对象使用引用比较

2.2.5.9 面试题
javascript
// 面试题1:以下代码输出什么?
let day = 3

switch (day) {
  case 1:
    console.log('A')
  case 2:
    console.log('B')
  case 3:
    console.log('C')
  case 4:
    console.log('D')
    break
  case 5:
    console.log('E')
}
// 输出:C D
// 解释:day = 3 匹配 case 3,输出 C,由于没有 break,继续执行 case 4,输出 D,遇到 break 结束

// 面试题2:以下代码输出什么?
let x = '10'

switch (x) {
  case 10:
    console.log('A')
    break
  case '10':
    console.log('B')
    break
  case 5 + 5:
    console.log('C')
    break
}
// 输出:B
// 解释:x = '10',严格匹配 '10' === '10' 为 true
// case 10:'10' === 10 为 false(类型不同)
// case 5 + 5:'10' === 10 为 false(5+5=10,数字10)

// 面试题3:以下代码输出什么?
let num = 2

switch (num) {
  case 1:
    console.log('A')
    break
  case 2:
    console.log('B')
  case 3:
    console.log('C')
  case 4:
    console.log('D')
    break
  default:
    console.log('E')
}
// 输出:B C D
// 解释:匹配 case 2,输出 B,继续执行 case 3、case 4

// 面试题4:以下代码输出什么?
let y = 0

switch (y) {
  case 1:
    console.log('A')
    break
  case 2:
    console.log('B')
    break
  case 3:
    console.log('C')
    break
}
// 无输出
// 解释:没有 default,y = 0 不匹配任何 case

// 面试题5:以下代码输出什么?
let a = 5

switch (a) {
  case 3:
    console.log('A')
    break
  case 4:
    console.log('B')
    break
  default:
    console.log('C')
    break
  case 5:
    console.log('D')
    break
}
// 输出:D
// 解释:a = 5 匹配 case 5,输出 D

// 面试题6:以下代码输出什么?
let score = 75

switch (true) {
  case score >= 90:
    console.log('A')
    break
  case score >= 80:
    console.log('B')
    break
  case score >= 70:
    console.log('C')
    break
  case score >= 60:
    console.log('D')
    break
  default:
    console.log('E')
}
// 输出:C
// 解释:score >= 90、>= 80 为 false,score >= 70 为 true

// 面试题7:以下代码输出什么?
let month = 5

switch (month) {
  case 3:
  case 4:
  case 5:
    console.log('春季')
    break
  case 6:
  case 7:
  case 8:
    console.log('夏季')
    break
  default:
    console.log('其他季节')
}
// 输出:春季
// 解释:利用 case 穿透,3、4、5 都执行相同的代码

// 面试题8:以下代码输出什么?
let str = 'hello'

switch (str) {
  case 'hello':
    console.log('A')
    break
  case 'Hello':
    console.log('B')
    break
  default:
    console.log('C')
}
// 输出:A
// 解释:'hello' 严格匹配 'hello',注意大小写

// 面试题9:以下代码输出什么?
let num1 = 1
let num2 = '1'

switch (num1) {
  case num2:
    console.log('A')
    break
  case 1:
    console.log('B')
    break
  default:
    console.log('C')
}
// 输出:C
// 解释:num1 = 1(数字),num2 = '1'(字符串)
// case num2:1 === '1' 为 false
// case 1:1 === 1 为 true,输出 B
// 等等,让我重新分析
// switch (num1) = switch (1)
// case num2 = case '1':1 === '1' 为 false
// case 1 = case 1:1 === 1 为 true,输出 B
// 所以输出应该是 B

// 面试题10:以下代码输出什么?
let x = 0

switch (x) {
  case 0:
    console.log('A')
  case false:
    console.log('B')
    break
  case null:
    console.log('C')
}
// 输出:A B
// 解释:x = 0 匹配 case 0,输出 A,由于没有 break,继续执行
// case false:0 === false 为 false
// 但这里有个问题,case 后面的值是 false,不是条件判断
// 实际上会执行 case false 这一行,但不会进入
// 让我重新理解:switch 使用严格相等 ===
// 0 === 0 为 true,执行 console.log('A')
// 没有遇到 break,继续向下执行 console.log('B')
// 遇到 break,结束
// 所以输出:A B

2.2.5.10 最佳实践

推荐做法

  1. 每个 case 都使用 break
javascript
// 推荐
switch (day) {
  case 1:
    console.log('周一')
    break
  case 2:
    console.log('周二')
    break
  default:
    console.log('其他')
}
  1. 使用 default 处理默认情况
javascript
// 推荐
switch (status) {
  case 'success':
    console.log('成功')
    break
  case 'error':
    console.log('错误')
    break
  default:
    console.log('未知状态')
}
  1. 有意使用 case 穿透时添加注释
javascript
// 推荐
switch (month) {
  case 3:
  case 4:
  case 5:
    console.log('春季')  // 3-5月为春季
    break
}
  1. 复杂条件考虑使用 if-else
javascript
// 复杂条件更适合用 if-else
if (age >= 18 && hasIdCard && score >= 60) {
  console.log('符合条件')
} else {
  console.log('不符合条件')
}

// 不推荐用 switch 处理这种情况
  1. 简单多值判断使用对象映射
javascript
// 推荐:简单映射使用对象
const dayMap = {
  1: '周一',
  2: '周二',
  3: '周三',
  4: '周四',
  5: '周五',
  6: '周六',
  7: '周日'
}
console.log(dayMap[day] || '无效')

避免做法

  1. 忘记 break(除非有意为之):
javascript
// 不推荐
switch (day) {
  case 1:
    console.log('周一')
    // 忘记 break
  case 2:
    console.log('周二')
}
  1. case 值重复
javascript
// 错误:重复的 case
switch (day) {
  case 1:
    console.log('周一')
    break
  case 1:  // 重复,报错
    console.log('一月')
    break
}
  1. 过度使用 switch
javascript
// 不推荐:简单二元判断
switch (true) {
  case age >= 18:
    console.log('成年')
    break
  default:
    console.log('未成年')
}

// 推荐:使用三元运算符
let level = age >= 18 ? '成年' : '未成年'
  1. switch 中有大量代码
javascript
// 不推荐:每个 case 有大量代码
switch (type) {
  case 'A':
    // 几百行代码...
    break
  case 'B':
    // 几百行代码...
    break
}

// 推荐:提取为函数
switch (type) {
  case 'A':
    handleTypeA()
    break
  case 'B':
    handleTypeB()
    break
}

2.2.5.11 switch 语句的适用场景

适合使用 switch 的场景

  1. 同一变量的多个离散值
javascript
switch (day) {
  case 1: case 2: case 3: case 4: case 5:
    console.log('工作日')
    break
  case 6: case 7:
    console.log('周末')
    break
}
  1. 多个 case 共用同一逻辑
javascript
switch (month) {
  case 3: case 4: case 5:
    console.log('春季')
    break
  case 6: case 7: case 8:
    console.log('夏季')
    break
}
  1. 状态机模式
javascript
switch (state) {
  case 'idle':
    console.log('空闲')
    break
  case 'running':
    console.log('运行中')
    break
  case 'paused':
    console.log('已暂停')
    break
  case 'stopped':
    console.log('已停止')
    break
}

不适合使用 switch 的场景

  1. 复杂的条件判断
javascript
// 不适合:复杂条件
switch (true) {
  case age >= 18 && hasIdCard:
    console.log('可以办理')
    break
  case age < 18:
    console.log('未成年')
    break
}

// 推荐:使用 if-else
if (age >= 18 && hasIdCard) {
  console.log('可以办理')
} else if (age < 18) {
  console.log('未成年')
}
  1. 简单的二元判断
javascript
// 不适合:简单二元判断
switch (true) {
  case age >= 18:
    console.log('成年')
    break
  default:
    console.log('未成年')
}

// 推荐:使用三元运算符
let level = age >= 18 ? '成年' : '未成年'
  1. 简单的键值映射
javascript
// 不适合:简单映射
switch (code) {
  case 1:
    console.log('成功')
    break
  case 2:
    console.log('失败')
    break
  case 3:
    console.log('进行中')
    break
}

// 推荐:使用对象映射
const statusMap = {
  1: '成功',
  2: '失败',
  3: '进行中'
}
console.log(statusMap[code])

2.2.5.12 总结

switch 语句要点

  1. 语法switch (表达式) { case 值: break; default: }
  2. 匹配:使用严格相等 === 进行匹配
  3. break:每个 case 推荐使用 break,防止 case 穿透
  4. 穿透:有意不使用 break 可以实现多个 case 共用同一代码
  5. default:可选,处理默认情况
  6. 适用场景:同一变量的多个离散值判断
  7. 优势:多值判断时代码清晰,可读性好
  8. 注意:记得使用 break,case 值不能重复

2.3 循环语句

在开发过程中,调试是发现和修复代码错误的重要技能。JavaScript 提供了多种调试方式,其中最常用的是断点调试和 console 方法。

2.3.1 断点调试

断点调试是一种强大的调试技术,它允许开发者在代码的特定位置暂停执行,以便检查变量值、执行流程和程序状态。

2.3.1.1 什么是断点调试

断点调试是通过在代码中设置"断点"(breakpoint),使程序执行到断点处暂停,从而可以逐步检查代码执行过程的一种调试方法。

核心概念

  • 断点:代码执行暂停的位置
  • 调试器:用于控制代码执行的工具(浏览器开发者工具)
  • 断点命中:程序执行到断点位置
  • 单步执行:一次执行一行代码

2.3.1.2 浏览器开发者工具

现代浏览器都内置了强大的开发者工具,其中包含断点调试功能。

如何打开开发者工具

方法1:快捷键

  • Windows/Linux:F12Ctrl + Shift + I
  • Mac:Cmd + Option + I

方法2:右键菜单

  1. 在网页上右键点击
  2. 选择"检查"或"审查元素"

方法3:浏览器菜单

  • Chrome:菜单 → 更多工具 → 开发者工具
  • Firefox:菜单 → Web 开发者 → Web 控制台

2.3.1.3 Sources 面板介绍

打开开发者工具后,切换到 Sources(资源)面板,这是断点调试的主要界面。

Sources 面板主要区域

┌─────────────────────────────────────────────────┐
│  页面文件导航区 (Navigator)                    │
│  ├─ localhost:5500                             │
│  │   ├─ index.html                            │
│  │   ├─ style.css                             │
│   │   └─ script.js ◄ 点击打开文件              │
├──────────────────┬──────────────────────────────┤
│   代码编辑区     │      调试控制区             │
│  (Editor Pane)   │     (Debug Pane)            │
│                  │                             │
│  let num = 10    │  Watch:                     │
│  debugger;       │    num: 10                  │
│  num += 5        │                             │
│  console.log()   │  Call Stack:                │
│                  │    (anonymous)             │
│                  │                             │
│                  │  Breakpoints:               │
│                  │    script.js:3              │
│                  │                             │
│                  │  Scope:                     │
│                  │    Local                    │
│                  │      num: 10                │
└──────────────────┴──────────────────────────────┘

img_17.png

各区域功能说明

区域名称功能
Navigator文件导航浏览和打开项目文件
Editor Pane代码编辑器显示和编辑代码,设置断点
Watch监视面板添加要监视的变量或表达式
Call Stack调用栈显示函数调用链
Breakpoints断点列表显示所有设置的断点
Scope作用域显示当前作用域的变量

2.3.1.4 设置断点的方法
方法1:在代码编辑器中点击行号
  1. 在 Sources 面板中打开要调试的文件
  2. 在代码行的行号上点击,会出现一个蓝色圆点(断点)
  3. 再次点击可以取消断点
javascript
// 示例代码:在行号 4 点击设置断点
let num = 10
let sum = 0
for (let i = 1; i <= 5; i++) {
  debugger;  //  ← 可以在这行设置断点
  sum += i
  console.log('sum:', sum)
}
方法2:在代码中使用 debugger 语句

debugger 语句会在代码执行到该位置时自动暂停,就像设置了一个断点。

javascript
function calculate(a, b) {
  let result = a + b
  debugger;  // 程序会在这里暂停,打开调试器
  result = result * 2
  return result
}

calculate(3, 5)  // 执行时会在 debugger 处暂停

注意

  • debugger 语句只在浏览器开发者工具打开时生效
  • 如果没有打开调试器,debugger 语句会被忽略
  • 可以在发布前的代码中移除所有 debugger 语句

2.3.1.5 断点调试的基本操作
1. 触发断点

断点设置后,需要执行代码才能触发:

javascript
// 示例:点击按钮触发
<button onclick="handleClick()">点击调试</button>

<script>
function handleClick() {
  let count = 0
  count++  // 在这里设置断点
  console.log(count)
}
</script>

触发方式

  • 刷新页面(如果代码在页面加载时执行)
  • 触发事件(如点击按钮)
  • 调用函数(如在控制台输入函数名并执行)
2. 调试控制按钮

当代码在断点处暂停时,Sources 面板顶部会出现调试控制按钮:

按钮名称快捷键功能
▶️继续F8 / Ctrl + F8继续执行,直到遇到下一个断点或程序结束
⏭️单步跳过F10执行当前行,不进入函数内部
⬇️单步进入F11执行当前行,如果遇到函数则进入函数内部
⬆️单步跳出Shift + F11执行当前函数剩余代码,返回调用处
⏸️暂停-暂停脚本执行
🔄重启Ctrl + Shift + F5重新启动调试

操作示例

javascript
function multiply(a, b) {
  return a * b
}

function calculate() {
  let x = 5
  let y = 3
  let z = multiply(x, y)  // 断点设置在这行
  console.log(z)
  return z
}

calculate()

// 调试流程:
// 1. 点击 F9 在 let z = multiply(x, y) 处设置断点
// 2. 刷新页面,程序在断点处暂停
// 3. 按 F10 单步跳过:执行当前行,z = 15
// 4. 按 F10 单步跳过:执行 console.log(z)
// 5. 按 F10 单步跳过:执行 return z
// 6. 程序结束

2.3.1.6 查看和修改变量
查看变量

在断点处暂停时,可以通过多种方式查看变量的值:

方法1:鼠标悬停

将鼠标悬停在代码中的变量上,会显示变量的值:

javascript
let num = 10
let result = num + 5
// 鼠标悬停在 num 上,会显示:num: 10
// 鼠标悬停在 result 上,会显示:result: 15

方法2:Scope(作用域)面板

Scope 面板会自动显示当前作用域的所有变量:

Scope:
├─ Local
│  ├─ num: 10
│  └─ result: 15
├─ Closure
└─ Global
   └─ window: Window

方法3:Watch(监视)面板

手动添加要监视的变量或表达式:

  1. 在 Watch 面板点击 +
  2. 输入变量名或表达式
  3. 可以监视多个变量
javascript
let a = 5
let b = 3
let c = a + b
let d = a * b

// Watch 面板可以监视:
// a         → 5
// b         → 3
// a + b     → 8
// a * b     → 15
// a > b     → true
// a % b     → 2

方法4:Console(控制台)面板

在控制台中输入变量名查看值:

javascript
// 在断点暂停时,在控制台输入:
console.log(num)    // 查看变量
num + 5            // 计算表达式
typeof num         // 查看类型
修改变量

在调试时可以修改变量的值,用于测试不同场景:

方法1:在 Scope 面板中直接修改

  1. 在 Scope 面板中找到变量
  2. 双击变量值
  3. 修改值并按回车

方法2:在控制台中赋值

javascript
// 在断点暂停时,在控制台输入:
num = 100        // 修改变量值
str = "hello"    // 修改变量值
javascript
// 实际应用示例
let age = 18

if (age >= 18) {
  console.log('已成年')
}

// 调试时,在断点处修改 age = 16
// 然后继续执行,会看到不同的输出

2.3.1.7 调用栈(Call Stack)

调用栈显示了函数的调用链,帮助理解程序的执行流程。

调用栈示例

javascript
function funcA() {
  funcB()  // 断点在这里
}

function funcB() {
  funcC()
}

function funcC() {
  debugger  // 暂停在这里
}

funcA()

// Call Stack 显示:
// funcC (script.js:10)
// funcB (script.js:6)
// funcA (script.js:2)
// (anonymous) (script.js:13)

调用栈的作用

  1. 查看函数调用顺序:了解代码是如何执行到当前位置的
  2. 导航到调用者:点击调用栈中的条目,可以跳转到对应的函数
  3. 理解递归:查看递归函数的调用深度
  4. 发现异常来源:当程序出错时,调用栈显示错误发生的位置

2.3.1.8 条件断点

条件断点只在满足特定条件时才会暂停执行,非常适合调试循环或特定场景。

设置条件断点
  1. 右键点击行号
  2. 选择"Add conditional breakpoint"(添加条件断点)
  3. 输入条件表达式
  4. 按回车确认
javascript
// 示例:循环调试
for (let i = 0; i < 1000; i++) {
  let result = calculate(i)
  console.log(i, result)
}

// 设置条件断点:只在 i === 500 时暂停
// 条件:i === 500

条件断点示例

javascript
// 示例1:调试特定循环次数
for (let i = 0; i < 100; i++) {
  console.log('当前是第', i, '次')
}
// 条件:i === 50  只在第50次循环时暂停

// 示例2:调试特定条件
let scores = [85, 92, 78, 65, 88]
for (let score of scores) {
  console.log('分数:', score)
}
// 条件:score < 70  只在分数低于70时暂停

// 示例3:复杂条件
function processUser(user) {
  if (user.age < 18) {
    console.log('未成年用户')
  } else if (user.role === 'admin') {
    console.log('管理员')
  }
}
// 条件:user.age < 18 && user.role === 'vip'

2.3.1.9 断点调试实战案例
案例1:调试数组遍历
javascript
// 问题:数组遍历结果不符合预期
let numbers = [1, 2, 3, 4, 5]
let sum = 0

for (let i = 0; i < numbers.length; i++) {
  sum += numbers[i]
  console.log('当前数字:', numbers[i], '累加和:', sum)
}

console.log('最终和:', sum)

调试步骤

  1. sum += numbers[i] 这行设置断点
  2. 刷新页面触发断点
  3. 使用 F10 单步跳过,观察每次循环:
    • i=0: sum=0+1=1
    • i=1: sum=1+2=3
    • i=2: sum=3+3=6
    • i=3: sum=6+4=10
    • i=4: sum=10+5=15
  4. 观察变量变化,确认逻辑正确
案例2:调试函数参数传递
javascript
function calculatePrice(price, discount, quantity) {
  let finalPrice = price * (1 - discount) * quantity
  return finalPrice
}

let totalPrice = calculatePrice(100, 0.2, 3)
console.log('总价:', totalPrice)

调试步骤

  1. function calculatePrice(price, discount, quantity) 行设置断点
  2. 触发断点后,查看 Scope 面板的参数:
    • price: 100
    • discount: 0.2
    • quantity: 3
  3. 单步执行,观察 finalPrice 的计算过程
  4. 确认计算逻辑是否正确
案例3:调试条件判断
javascript
function getGrade(score) {
  if (score >= 90) {
    return '优秀'
  } else if (score >= 80) {
    return '良好'
  } else if (score >= 70) {
    return '中等'
  } else if (score >= 60) {
    return '及格'
  } else {
    return '不及格'
  }
}

let myScore = 85
let grade = getGrade(myScore)
console.log('等级:', grade)

调试步骤

  1. if (score >= 90) 设置断点
  2. 触发断点,查看 score 的值(85)
  3. 单步跳过(F10),观察每个条件:
    • score >= 90: 85 >= 90 = false
    • score >= 80: 85 >= 80 = true ✓
  4. 确认返回 '良好'
案例4:调试嵌套循环
javascript
// 问题:打印乘法表
for (let i = 1; i <= 9; i++) {
  for (let j = 1; j <= i; j++) {
    console.log(`${i} × ${j} = ${i * j}`)
  }
}

调试步骤

  1. 在内层循环的 console.log 设置条件断点
  2. 设置条件:i === 3 && j === 2(只在特定组合时暂停)
  3. 观察循环变量的变化规律
  4. 理解嵌套循环的执行流程

2.3.1.10 断点调试技巧
技巧1:使用条件断点跳过大量循环
javascript
// 避免在每次循环都暂停
for (let i = 0; i < 10000; i++) {
  processData(i)
}
// 设置条件断点:i % 100 === 0  每100次暂停一次
技巧2:使用 Logpoints(日志点)

日志点不会暂停执行,只会在控制台输出信息:

  1. 右键点击行号
  2. 选择"Add logpoint"(添加日志点)
  3. 输入要输出的表达式
javascript
let user = { name: '小明', age: 18 }
let isValid = checkUser(user)

// 设置日志点:`用户:${user.name},验证结果:${isValid}`
// 控制台输出:用户:小明,验证结果:true
技巧3:使用 Watch 监视复杂表达式
javascript
let students = [
  { name: '小明', score: 85 },
  { name: '小红', score: 92 },
  { name: '小刚', score: 78 }
]

// Watch 中添加:
// students.length              → 3
// students[0].score            → 85
// students.filter(s => s.score >= 80).length  → 2
// students.map(s => s.score).reduce((a, b) => a + b)  → 255
技巧4:使用 Blackbox Script(黑盒脚本)

将第三方库或框架代码标记为黑盒,调试时会自动跳过:

  1. 在 Sources 面板右键点击文件
  2. 选择"Blackbox script"
  3. 单步执行时会自动跳过该文件

2.3.1.11 调试常见问题
问题1:断点没有命中

可能原因

  1. 代码没有被执行到(如条件不满足)
  2. 文件被修改后没有刷新页面
  3. 使用了代码压缩或混淆
  4. 断点设置在注释或空行

解决方法

javascript
// 添加 debugger 语句确认代码是否执行
function test() {
  debugger;  // 如果这行不暂停,说明函数没有被调用
  console.log('代码执行到这里')
}
问题2:变量显示 undefined

可能原因

  1. 变量在当前作用域不存在
  2. 变量在断点之后才声明
  3. 代码执行流程跳过了变量声明

解决方法

javascript
// 检查变量声明位置
let num = 10
debugger;  // 在这里可以看到 num

console.log(num)  // 如果断点设在这里,num 已经声明
问题3:无法查看闭包变量

可能原因: 闭包变量不在当前的 Scope 中,需要向上查找作用域链。

解决方法

javascript
function outer() {
  let privateVar = 100
  
  return function inner() {
    debugger;  // 在这里,privateVar 在 Closure 作用域中
    console.log(privateVar)
  }
}

const fn = outer()
fn()  // 在断点处查看 Scope → Closure → privateVar

2.3.1.12 断点调试 vs console.log

对比表格

特性断点调试console.log
暂停执行✓ 可以✗ 不可以
查看变量✓ 实时查看✓ 手动输出
修改变量✓ 可以✗ 不可以
单步执行✓ 支持✗ 不支持
查看调用栈✓ 支持✗ 不支持
条件调试✓ 条件断点✗ 需要手动判断
性能影响中等(暂停时)较小
使用场景复杂逻辑调试快速查看值

使用建议

javascript
// 使用 console.log:快速查看变量值
let sum = calculate()
console.log('计算结果:', sum)

// 使用断点调试:复杂逻辑分析
function complexLogic(data) {
  debugger;  // 在这里暂停,逐步检查每一步
  let processed = processData(data)
  let filtered = filterData(processed)
  let result = transformData(filtered)
  return result
}

2.3.1.13 调试快捷键总结
快捷键功能说明
F8 / Ctrl + F8切换断点在当前行添加或移除断点
F9继续执行继续执行到下一个断点
F10单步跳过执行当前行,不进入函数
F11单步进入执行当前行,进入函数内部
Shift + F11单步跳出跳出当前函数
Ctrl + Shift + F5重启调试重新开始调试
Ctrl + P搜索文件在项目中搜索文件
Ctrl + Shift + F全局搜索在所有文件中搜索
Ctrl + G跳转行号跳转到指定行

2.3.1.14 面试题
javascript
// 面试题1:以下代码输出什么?如何使用断点调试验证?
let i = 0
while (i < 3) {
  i++
}
console.log(i)
// 输出:3
// 调试方法:在 i++ 设置断点,观察 i 的变化:0→1→2→3

// 面试题2:如何调试递归函数?
function factorial(n) {
  if (n <= 1) return 1
  return n * factorial(n - 1)
}
console.log(factorial(5))
// 调试方法:
// 1. 在 return n * factorial(n - 1) 设置断点
// 2. 观察调用栈,查看递归深度
// 3. 单步进入,观察每次递归的 n 值

// 面试题3:如何调试闭包?
function counter() {
  let count = 0
  return function() {
    count++
    return count
  }
}

const c1 = counter()
const c2 = counter()
console.log(c1())  // 1
console.log(c1())  // 2
console.log(c2())  // 1
// 调试方法:
// 1. 在 count++ 设置断点
// 2. 查看调用栈,确认调用的是 c1 还是 c2
// 3. 查看 Scope → Closure → count 的值

// 面试题4:如何调试异步代码?
setTimeout(() => {
  console.log('定时器执行')
}, 1000)
console.log('同步代码')
// 调试方法:
// 1. 在定时器回调函数设置断点
// 2. 观察调用栈,确认是异步回调
// 3. 注意异步代码的执行时机

// 面试题5:如何调试事件处理?
document.getElementById('btn').addEventListener('click', function() {
  console.log('按钮被点击')
})
// 调试方法:
// 1. 在事件处理函数设置断点
// 2. 点击按钮触发断点
// 3. 查看事件对象 event,了解事件详情

2.3.1.15 最佳实践

推荐做法

  1. 合理设置断点
javascript
// 推荐:在关键逻辑处设置断点
if (user.loginStatus === 'success') {
  debugger;  // 在这里检查登录后的逻辑
  redirectToHome()
}
  1. 使用条件断点提高效率
javascript
// 推荐:避免在每次循环都暂停
for (let i = 0; i < 10000; i++) {
  // 条件断点:i === 5000
  processItem(i)
}
  1. 利用 Watch 面板监控变量
javascript
// 推荐:添加复杂表达式到 Watch
// Watch: users.filter(u => u.active).length
// Watch: performance.now()
  1. 调试完成后清理
javascript
// 记得移除 debugger 语句
// function test() {
//   debugger;  // ← 发布前删除
//   return result
// }
  1. 使用日志点替代部分断点
javascript
// 推荐:使用日志点记录信息,不中断执行
// Logpoint: `处理到第 ${i} 项,值:${value}`
for (let item of items) {
  processItem(item)
}

避免做法

  1. 避免过多的断点
javascript
// 不推荐:在每行都设置断点,影响调试效率
let a = 1  // 断点
let b = 2  // 断点
let c = 3  // 断点
// 推荐:只在关键位置设置断点
  1. 避免忘记清理调试代码
javascript
// 不推荐:发布代码中包含 debugger
function APIHandler() {
  debugger;  // ← 发布前必须删除
  return fetch(...)
}
  1. 避免过度依赖断点调试
javascript
// 不推荐:用断点调试解决所有问题
// 简单的值查看应该用 console.log
let result = calculate()
debugger;  // 过度使用
// 推荐:简单查看用 console.log
console.log(result)

2.3.1.16 总结

断点调试要点

  1. 定义:在代码中设置断点,暂停程序执行以检查状态
  2. 工具:使用浏览器开发者工具的 Sources 面板
  3. 操作:设置断点、单步执行、查看变量、修改变量
  4. 技巧:条件断点、日志点、监视表达式
  5. 场景:复杂逻辑、循环、递归、闭包、异步代码
  6. 优势:可以暂停执行、单步调试、查看调用栈、实时修改
  7. 注意:调试完成后记得移除调试代码

核心概念

  • 断点是代码执行暂停的位置
  • 单步执行可以逐步检查代码
  • Scope 面板显示当前作用域的所有变量
  • Call Stack 显示函数调用链
  • 条件断点可以只在特定条件时暂停

2.3.2 while 循环

while 循环是最基本的循环结构,它会在条件为 true 时重复执行代码块,直到条件变为 false

2.3.2.1 基本语法
javascript
while (条件) {
  // 循环体:当条件为 true 时重复执行的代码
}

语法说明

  • while:关键字,表示循环
  • 条件:一个表达式,会被转换为布尔值
  • {}:循环体,包含要重复执行的代码
  • 执行流程:先判断条件,如果为 true 则执行循环体,然后再次判断条件
  • 循环结束:当条件变为 false 时,退出循环

2.3.2.2 执行流程
mermaid
流程图:
┌─────────┐
│  开始    │
└────┬────┘


┌─────────┐
│ 判断条件 │
└────┬────┘

     ├─── true ───┐
     │            ▼
     │      ┌──────────┐
     │      │ 执行循环体 │
     │      └────┬─────┘
     │           │
     │           ▼
     │      ┌──────────┐
     │      │ 返回判断  │
     │      └────┬─────┘
     │           │
     │           │
     └───────────┘

     └── false ──┤


             ┌──────────┐
             │ 退出循环  │
             └──────────┘

执行特点

  1. 先判断,后执行:条件不满足时,循环体一次都不会执行
  2. 循环次数不确定:根据条件决定循环次数
  3. 需要手动更新条件:在循环体中改变条件值,否则会造成死循环

2.3.2.3 代码示例
示例1:基础用法
javascript
// 打印 1 到 5
let i = 1
while (i <= 5) {
  console.log(i)
  i++
}
// 输出:
// 1
// 2
// 3
// 4
// 5
javascript
// 计算 1 到 10 的和
let sum = 0
let i = 1
while (i <= 10) {
  sum += i
  i++
}
console.log('1到10的和:', sum)  // 1到10的和: 55
javascript
// 计算阶乘 5! = 5 × 4 × 3 × 2 × 1
let factorial = 1
let n = 5
let i = 1
while (i <= n) {
  factorial *= i
  i++
}
console.log('5的阶乘:', factorial)  // 5的阶乘: 120
示例2:倒序输出
javascript
// 从 5 倒序打印到 1
let i = 5
while (i >= 1) {
  console.log(i)
  i--
}
// 输出:
// 5
// 4
// 3
// 2
// 1
javascript
// 倒序输出数组元素
let fruits = ['苹果', '香蕉', '橙子', '葡萄']
let i = fruits.length - 1
while (i >= 0) {
  console.log(fruits[i])
  i--
}
// 输出:
// 葡萄
// 橙子
// 香蕉
// 苹果
示例3:字符串操作
javascript
// 统计字符串中字符的个数
let str = 'Hello World'
let count = 0
let i = 0
while (i < str.length) {
  count++
  i++
}
console.log('字符串长度:', count)  // 字符串长度: 11
javascript
// 查找字符首次出现的位置
let text = 'JavaScript'
let target = 'a'
let foundIndex = -1
let i = 0
while (i < text.length && foundIndex === -1) {
  if (text[i] === target) {
    foundIndex = i
  }
  i++
}
console.log('字符 a 的位置:', foundIndex)  // 字符 a 的位置: 1
示例4:数组操作
javascript
// 计算数组元素的平均值
let numbers = [85, 90, 78, 92, 88]
let sum = 0
let i = 0
while (i < numbers.length) {
  sum += numbers[i]
  i++
}
let average = sum / numbers.length
console.log('平均分:', average)  // 平均分: 86.6
javascript
// 查找数组中的最大值
let nums = [23, 45, 12, 67, 34, 89]
let max = nums[0]
let i = 1
while (i < nums.length) {
  if (nums[i] > max) {
    max = nums[i]
  }
  i++
}
console.log('最大值:', max)  // 最大值: 89
javascript
// 筛选数组中的偶数
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evens = []
let i = 0
while (i < arr.length) {
  if (arr[i] % 2 === 0) {
    evens.push(arr[i])
  }
  i++
}
console.log('偶数数组:', evens)  // 偶数数组: [2, 4, 6, 8, 10]
示例5:数学计算
javascript
// 判断素数
function isPrime(num) {
  if (num <= 1) return false
  if (num === 2) return true
  
  let i = 2
  while (i <= Math.sqrt(num)) {
    if (num % i === 0) {
      return false
    }
    i++
  }
  return true
}

console.log(isPrime(7))   // true
console.log(isPrime(10))  // false
console.log(isPrime(17))  // true
javascript
// 斐波那契数列前 n 项
function fibonacci(n) {
  if (n <= 0) return []
  if (n === 1) return [0]
  if (n === 2) return [0, 1]
  
  let result = [0, 1]
  let i = 2
  while (i < n) {
    result.push(result[i-1] + result[i-2])
    i++
  }
  return result
}

console.log(fibonacci(10))
// 输出: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
javascript
// 计算最大公约数(欧几里得算法)
function gcd(a, b) {
  while (b !== 0) {
    let temp = b
    b = a % b
    a = temp
  }
  return a
}

console.log(gcd(48, 18))  // 6
console.log(gcd(100, 75)) // 25
示例6:条件判断循环
javascript
// 输入验证(模拟)
let password = ''
let attempts = 0
const maxAttempts = 3

while (password !== '123456' && attempts < maxAttempts) {
  console.log('请输入密码:')
  password = '123456'  // 模拟输入正确密码
  attempts++
  console.log(`尝试次数: ${attempts}`)
}

if (password === '123456') {
  console.log('密码正确,登录成功!')
} else {
  console.log('尝试次数超限,登录失败!')
}
javascript
// 寻找第一个大于 1000 的 2 的幂
let power = 1
let exponent = 0
while (power <= 1000) {
  power *= 2
  exponent++
}
console.log(`2 的 ${exponent} 次方 = ${power}`)  // 2 的 10 次方 = 1024

2.3.2.4 注意事项
重要注意事项
  1. 必须更新循环变量
javascript
// 正确示例
let i = 0
while (i < 5) {
  console.log(i)
  i++  // 必须更新变量,否则死循环
}

// 错误示例:死循环
let i = 0
while (i < 5) {
  console.log(i)
  // 忘记更新 i,造成死循环
}
  1. 循环条件要能变为 false
javascript
// 正确示例
let count = 10
while (count > 0) {
  console.log(count)
  count--
}

// 错误示例:永远为 true
let count = 10
while (count > 0) {
  console.log(count)
  // count 没有变化,条件永远为 true
}
  1. 初始化循环变量
javascript
// 正确示例
let i = 0
while (i < 5) {
  console.log(i)
  i++
}

// 错误示例:未初始化
let i
while (i < 5) {  // i 是 undefined,条件判断可能出错
  console.log(i)
  i++
}
  1. 避免死循环
javascript
// 死循环示例1:条件永远为 true
while (true) {
  console.log('这会一直执行')
  // 需要手动 break 才能退出
}

// 死循环示例2:条件永远不会改变
let flag = true
while (flag) {
  console.log('执行中')
  // flag 没有改变,死循环
}

// 正确做法:使用 break
let flag = true
let count = 0
while (flag) {
  console.log('执行中')
  count++
  if (count >= 5) {
    break  // 手动退出循环
  }
}

2.3.2.5 实际应用场景
场景1:用户输入验证
javascript
// 模拟用户输入,直到输入有效值
let input = ''
while (input.length < 3 || input.length > 10) {
  console.log('请输入用户名(3-10个字符):')
  input = 'admin'  // 模拟输入
}
console.log('用户名有效:', input)
javascript
// 猜数字游戏
let target = Math.floor(Math.random() * 100) + 1
let guess = 0
let attempts = 0

console.log('猜一个1到100之间的数字')

while (guess !== target) {
  attempts++
  guess = 50  // 模拟猜测
  
  if (guess < target) {
    console.log('太小了!')
  } else if (guess > target) {
    console.log('太大了!')
  }
}

console.log(`恭喜!你用了 ${attempts} 次猜对了!`)
场景2:数据处理
javascript
// 过滤并转换数据
let students = [
  { name: '小明', score: 85 },
  { name: '小红', score: 92 },
  { name: '小刚', score: 78 },
  { name: '小丽', score: 65 }
]

let excellentStudents = []
let i = 0
while (i < students.length) {
  if (students[i].score >= 90) {
    excellentStudents.push({
      name: students[i].name,
      score: students[i].score,
      grade: '优秀'
    })
  }
  i++
}

console.log('优秀学生:', excellentStudents)
javascript
// 数组去重
let arr = [1, 2, 3, 2, 4, 5, 3, 6, 1]
let uniqueArr = []
let i = 0

while (i < arr.length) {
  let isDuplicate = false
  let j = 0
  while (j < uniqueArr.length) {
    if (arr[i] === uniqueArr[j]) {
      isDuplicate = true
      break
    }
    j++
  }
  if (!isDuplicate) {
    uniqueArr.push(arr[i])
  }
  i++
}

console.log('去重后的数组:', uniqueArr)  // [1, 2, 3, 4, 5, 6]
场景3:搜索算法
javascript
// 二分查找
function binarySearch(arr, target) {
  let left = 0
  let right = arr.length - 1
  
  while (left <= right) {
    let mid = Math.floor((left + right) / 2)
    
    if (arr[mid] === target) {
      return mid
    } else if (arr[mid] < target) {
      left = mid + 1
    } else {
      right = mid - 1
    }
  }
  
  return -1  // 未找到
}

let sortedArray = [1, 3, 5, 7, 9, 11, 13, 15]
console.log('查找 7 的位置:', binarySearch(sortedArray, 7))   // 3
console.log('查找 10 的位置:', binarySearch(sortedArray, 10)) // -1
javascript
// 线性查找
function linearSearch(arr, target) {
  let i = 0
  while (i < arr.length) {
    if (arr[i] === target) {
      return i
    }
    i++
  }
  return -1
}

let array = [4, 2, 7, 1, 9, 3]
console.log('查找 7 的位置:', linearSearch(array, 7))   // 2
console.log('查找 5 的位置:', linearSearch(array, 5))   // -1
场景4:游戏开发
javascript
// 倒计时
function countdown(seconds) {
  while (seconds > 0) {
    console.log(`${seconds} 秒`)
    seconds--
    // 这里可以添加延迟,如 setTimeout
  }
  console.log('时间到!')
}

countdown(5)
// 输出:
// 5 秒
// 4 秒
// 3 秒
// 2 秒
// 1 秒
// 时间到!
javascript
// 简单的生命值系统
let health = 100
let damage = 15

while (health > 0) {
  console.log(`当前生命值: ${health}`)
  health -= damage
  if (health < 0) {
    health = 0
  }
}
console.log('游戏结束!生命值归零')

2.3.2.6 while vs for

对比表格

特性while 循环for 循环
语法while(条件){}for(初始化; 条件; 更新){}
初始化需要在循环外单独定义在循环头部定义
更新需要在循环体中手动更新自动执行更新语句
循环次数不确定,由条件决定通常确定,由计数器决定
使用场景条件复杂、次数不确定次数确定、简单计数
可读性适合复杂条件适合固定次数循环
灵活性更灵活结构更紧凑

使用建议

javascript
// 使用 for:次数确定
for (let i = 0; i < 10; i++) {
  console.log(i)
}

// 使用 while:条件复杂或次数不确定
let i = 0
while (shouldContinue(i)) {
  console.log(i)
  i++
}

2.3.2.7 面试题
javascript
// 面试题1:以下代码输出什么?
let i = 0
while (i < 3) {
  i++
  console.log(i)
}
// 输出:
// 1
// 2
// 3
// 解释:i 先自增,再输出,所以输出 1, 2, 3

// 面试题2:以下代码输出什么?
let n = 10
while (n > 0) {
  if (n % 2 === 0) {
    console.log(n)
  }
  n--
}
// 输出:
// 10
// 8
// 6
// 4
// 2
// 解释:只输出偶数

// 面试题3:以下代码执行多少次循环?
let i = 1
let count = 0
while (i < 100) {
  i *= 2
  count++
}
console.log('循环次数:', count)
// 输出:循环次数: 7
// 解释:1→2→4→8→16→32→64→128,共7次

// 面试题4:以下代码输出什么?
let x = 1
let y = 0
while (x <= 5) {
  y += x
  x++
}
console.log(y)
// 输出:15
// 解释:1+2+3+4+5 = 15

// 面试题5:找出问题
let i = 0
while (i < 5) {
  console.log(i)
  // 忘记更新 i,死循环
}
// 问题:没有更新循环变量,造成死循环

// 面试题6:输出斐波那契数列前5项
let a = 0, b = 1
let count = 0
while (count < 5) {
  console.log(a)
  let temp = a + b
  a = b
  b = temp
  count++
}
// 输出:0, 1, 1, 2, 3

// 面试题7:判断数字是否为回文数
function isPalindrome(num) {
  let original = num
  let reversed = 0
  
  while (num > 0) {
    let digit = num % 10
    reversed = reversed * 10 + digit
    num = Math.floor(num / 10)
  }
  
  return original === reversed
}

console.log(isPalindrome(121))  // true
console.log(isPalindrome(123))  // false

// 面试题8:计算数字的位数
function countDigits(num) {
  if (num === 0) return 1
  
  num = Math.abs(num)
  let count = 0
  while (num > 0) {
    num = Math.floor(num / 10)
    count++
  }
  return count
}

console.log(countDigits(12345))  // 5
console.log(countDigits(-100))   // 3

2.3.2.8 最佳实践

推荐做法

  1. 初始化循环变量
javascript
// 推荐
let i = 0
while (i < 10) {
  // 代码
  i++
}
  1. 确保条件能变为 false
javascript
// 推荐
let count = 10
while (count > 0) {
  // 代码
  count--  // 条件会变化
}
  1. 使用有意义的变量名
javascript
// 推荐
let attempt = 1
while (attempt <= maxAttempts) {
  // 代码
  attempt++
}

// 不推荐
let i = 1
while (i <= n) {
  // 代码
  i++
}
  1. 循环前检查边界条件
javascript
// 推荐
function findElement(arr, target) {
  if (!arr || arr.length === 0) {
    return -1
  }
  
  let i = 0
  while (i < arr.length) {
    if (arr[i] === target) {
      return i
    }
    i++
  }
  return -1
}
  1. 限制循环次数(防止死循环)
javascript
// 推荐
let maxIterations = 1000
let iterations = 0
while (condition && iterations < maxIterations) {
  // 代码
  iterations++
}

if (iterations >= maxIterations) {
  console.log('警告:可能存在死循环')
}

避免做法

  1. 避免死循环
javascript
// 不推荐:死循环
while (true) {
  // 永远执行
}

// 推荐:使用 break
let count = 0
while (true) {
  if (count >= 10) break
  count++
}
  1. 避免忘记更新变量
javascript
// 不推荐
let i = 0
while (i < 10) {
  console.log(i)
  // 忘记更新 i
}

// 推荐
let i = 0
while (i < 10) {
  console.log(i)
  i++
}
  1. 避免复杂的循环条件
javascript
// 不推荐:条件太复杂
while (i < 10 && j > 0 && flag && count < 100 && !done) {
  // 代码
}

// 推荐:拆分为变量
let shouldContinue = i < 10 && j > 0 && flag && count < 100 && !done
while (shouldContinue) {
  // 代码
  shouldContinue = i < 10 && j > 0 && flag && count < 100 && !done
}

2.3.2.9 总结

while 循环要点

  1. 语法while (条件) { 循环体 }
  2. 执行:先判断条件,满足则执行循环体
  3. 特点:先判断后执行,可能一次都不执行
  4. 注意:必须更新循环变量,否则死循环
  5. 使用场景:循环次数不确定、条件复杂
  6. 对比:比 for 更灵活,但需要手动管理循环变量
  7. 最佳实践:初始化变量、更新变量、防止死循环

核心要点

  • while 循环在条件为 true 时重复执行
  • 必须在循环体中更新条件变量
  • 循环次数不固定,由条件决定
  • 适合不确定次数的循环
  • 记得处理边界条件

2.3.3 for 循环

for 循环是 JavaScript 中最常用的循环结构,它将初始化、条件判断和循环变量更新集中在循环头部,代码结构清晰简洁。

2.3.3.1 基本语法
javascript
for (初始化; 条件; 更新) {
  // 循环体:当条件为 true 时重复执行的代码
}

语法说明

部分说明示例
初始化循环开始前执行一次,通常用于声明和初始化循环变量let i = 0
条件每次循环开始前判断,如果为 true 则执行循环体,否则退出循环i < 5
更新每次循环结束后执行,通常用于更新循环变量i++
循环体当条件为 true 时重复执行的代码块{ console.log(i) }

执行流程

  1. 执行初始化(只执行一次)
  2. 判断条件,如果为 true 则执行循环体,如果为 false 则退出循环
  3. 执行循环体
  4. 执行更新
  5. 返回步骤 2

2.3.3.2 执行流程
流程图:
┌─────────┐
│  开始    │
└────┬────┘


┌─────────┐
│ 初始化   │ (只执行一次)
└────┬────┘


┌─────────┐
│ 判断条件 │
└────┬────┘

     ├─── true ───┐
     │            ▼
     │      ┌──────────┐
     │      │ 执行循环体 │
     │      └────┬─────┘
     │           │
     │           ▼
     │      ┌──────────┐
     │      │ 更新变量  │
     │      └────┬─────┘
     │           │
     │           │
     └───────────┤

     └── false ──┤


             ┌──────────┐
             │ 退出循环  │
             └──────────┘

2.3.3.3 代码示例
示例1:基础用法
javascript
// 打印 0 到 4
for (let i = 0; i < 5; i++) {
  console.log(i)
}
// 输出:
// 0
// 1
// 2
// 3
// 4

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

// 倒序打印 5 到 1
for (let i = 5; i >= 1; i--) {
  console.log(i)
}
// 输出:
// 5
// 4
// 3
// 2
// 1
示例2:累加和累乘
javascript
// 计算 1 到 100 的和
let sum = 0
for (let i = 1; i <= 100; i++) {
  sum += i
}
console.log('1到100的和:', sum)  // 1到100的和: 5050

// 计算 1 到 10 的乘积(阶乘)
let factorial = 1
for (let i = 1; i <= 10; i++) {
  factorial *= i
}
console.log('10的阶乘:', factorial)  // 10的阶乘: 3628800

// 计算偶数和
let evenSum = 0
for (let i = 2; i <= 100; i += 2) {
  evenSum += i
}
console.log('1到100的偶数和:', evenSum)  // 1到100的偶数和: 2550

// 计算奇数和
let oddSum = 0
for (let i = 1; i <= 100; i += 2) {
  oddSum += i
}
console.log('1到100的奇数和:', oddSum)  // 1到100的奇数和: 2500
示例3:数组遍历
javascript
// 遍历数组元素
let fruits = ['苹果', '香蕉', '橙子', '葡萄']
for (let i = 0; i < fruits.length; i++) {
  console.log('第' + i + '个水果:', fruits[i])
}
// 输出:
// 第0个水果: 苹果
// 第1个水果: 香蕉
// 第2个水果: 橙子
// 第3个水果: 葡萄

// 计算数组元素的平均值
let numbers = [85, 90, 78, 92, 88]
let total = 0
for (let i = 0; i < numbers.length; i++) {
  total += numbers[i]
}
let avg = total / numbers.length
console.log('平均分:', avg)  // 平均分: 86.6

// 查找数组中的最大值
let scores = [23, 45, 67, 12, 89, 34]
let max = scores[0]
for (let i = 1; i < scores.length; i++) {
  if (scores[i] > max) {
    max = scores[i]
  }
}
console.log('最大值:', max)  // 最大值: 89

// 筛选偶数
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evens = []
for (let i = 0; i < arr.length; i++) {
  if (arr[i] % 2 === 0) {
    evens.push(arr[i])
  }
}
console.log('偶数数组:', evens)  // 偶数数组: [2, 4, 6, 8, 10]
示例4:字符串操作
javascript
// 统计字符串中的字符数
let str = 'Hello World'
let count = 0
for (let i = 0; i < str.length; i++) {
  count++
}
console.log('字符个数:', count)  // 字符个数: 11

// 统计大写字母个数
let text = 'Hello World'
let upperCount = 0
for (let i = 0; i < text.length; i++) {
  if (text[i] >= 'A' && text[i] <= 'Z') {
    upperCount++
  }
}
console.log('大写字母个数:', upperCount)  // 大写字母个数: 2

// 查找字符位置
let message = 'JavaScript'
let target = 'a'
let positions = []
for (let i = 0; i < message.length; i++) {
  if (message[i] === target) {
    positions.push(i)
  }
}
console.log('字符 a 的位置:', positions)  // 字符 a 的位置: [1, 3]

// 反转字符串
let original = 'hello'
let reversed = ''
for (let i = original.length - 1; i >= 0; i--) {
  reversed += original[i]
}
console.log('反转后:', reversed)  // 反转后: olleh
示例5:乘法表
javascript
// 九九乘法表
for (let i = 1; i <= 9; i++) {
  let line = ''
  for (let j = 1; j <= i; j++) {
    line += `${i} × ${j} = ${i * j}  `
  }
  console.log(line)
}
// 输出:
// 1 × 1 = 1  
// 2 × 1 = 2  2 × 2 = 4  
// 3 × 1 = 3  3 × 2 = 6  3 × 3 = 9  
// ...(以此类推)
示例6:数学计算
javascript
// 判断素数
function isPrime(n) {
  if (n <= 1) return false
  for (let i = 2; i <= Math.sqrt(n); i++) {
    if (n % i === 0) {
      return false
    }
  }
  return true
}

console.log(isPrime(7))   // true
console.log(isPrime(10))  // false
console.log(isPrime(17))  // true

// 打印斐波那契数列前 10 项
let n = 10
let a = 0, b = 1
console.log('斐波那契数列前' + n + '项:')
for (let i = 0; i < n; i++) {
  console.log(a)
  let temp = a + b
  a = b
  b = temp
}
// 输出:
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34

// 打印水仙花数(三位数,各位数字的立方和等于本身)
console.log('水仙花数:')
for (let i = 100; i <= 999; i++) {
  let hundreds = Math.floor(i / 100)
  let tens = Math.floor((i % 100) / 10)
  let ones = i % 10
  
  if (hundreds ** 3 + tens ** 3 + ones ** 3 === i) {
    console.log(i)
  }
}
// 输出:
// 153
// 370
// 371
// 407

2.3.3.4 for 循环变体
省略各个部分

for 循环的三个部分都是可选的,可以省略。

javascript
// 省略初始化
let i = 0
for (; i < 5; i++) {
  console.log(i)
}

// 省略条件(会变成无限循环,需要手动 break)
for (let i = 0; ; i++) {
  if (i >= 5) break
  console.log(i)
}

// 省略更新
for (let i = 0; i < 5; ) {
  console.log(i)
  i++
}

// 省略所有(无限循环)
for (;;) {
  console.log('无限循环')
  break
}

注意:虽然可以省略部分内容,但一般情况下不建议这样做,除非有特殊需求。


2.3.3.5 实际应用场景
场景1:批量操作
javascript
// 批量创建元素
let container = document.getElementById('container')
for (let i = 0; i < 5; i++) {
  let div = document.createElement('div')
  div.textContent = '元素 ' + i
  container.appendChild(div)
}

// 批量生成数据
let users = []
for (let i = 1; i <= 10; i++) {
  users.push({
    id: i,
    name: '用户' + i,
    age: Math.floor(Math.random() * 50) + 18
  })
}
console.log(users)
场景2:数据统计
javascript
// 统计考试成绩
let scores = [85, 92, 78, 65, 88, 95, 72, 80]
let excellent = 0  // 优秀
let good = 0       // 良好
let pass = 0       // 及格
let fail = 0       // 不及格

for (let i = 0; i < scores.length; i++) {
  if (scores[i] >= 90) {
    excellent++
  } else if (scores[i] >= 80) {
    good++
  } else if (scores[i] >= 60) {
    pass++
  } else {
    fail++
  }
}

console.log('优秀:', excellent)
console.log('良好:', good)
console.log('及格:', pass)
console.log('不及格:', fail)
场景3:数组去重
javascript
// 数组去重
let arr = [1, 2, 3, 2, 4, 5, 3, 6, 1]
let uniqueArr = []

for (let i = 0; i < arr.length; i++) {
  let isDuplicate = false
  for (let j = 0; j < uniqueArr.length; j++) {
    if (arr[i] === uniqueArr[j]) {
      isDuplicate = true
      break
    }
  }
  if (!isDuplicate) {
    uniqueArr.push(arr[i])
  }
}

console.log('去重后的数组:', uniqueArr)  // [1, 2, 3, 4, 5, 6]
场景4:冒泡排序
javascript
// 冒泡排序
function bubbleSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        // 交换位置
        let temp = arr[j]
        arr[j] = arr[j + 1]
        arr[j + 1] = temp
      }
    }
  }
  return arr
}

let numbers = [64, 34, 25, 12, 22, 11, 90]
console.log('排序前:', numbers)
console.log('排序后:', bubbleSort([...numbers]))

2.3.3.6 嵌套 for 循环

嵌套循环是指在一个循环内部再包含另一个循环。

javascript
// 打印矩形
for (let i = 0; i < 3; i++) {
  let line = ''
  for (let j = 0; j < 5; j++) {
    line += '* '
  }
  console.log(line)
}
// 输出:
// * * * * * 
// * * * * * 
// * * * * * 

// 打印直角三角形
for (let i = 1; i <= 5; i++) {
  let line = ''
  for (let j = 1; j <= i; j++) {
    line += '* '
  }
  console.log(line)
}
// 输出:
// * 
// * * 
// * * * 
// * * * * 
// * * * * * 

// 打印等腰三角形
for (let i = 1; i <= 5; i++) {
  let line = ''
  // 打印空格
  for (let j = 1; j <= 5 - i; j++) {
    line += ' '
  }
  // 打印星号
  for (let j = 1; j <= 2 * i - 1; j++) {
    line += '*'
  }
  console.log(line)
}
// 输出:
//     *
//    ***
//   *****
//  *******
// *********

// 二维数组遍历
let matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

for (let i = 0; i < matrix.length; i++) {
  let row = ''
  for (let j = 0; j < matrix[i].length; j++) {
    row += matrix[i][j] + ' '
  }
  console.log(row)
}
// 输出:
// 1 2 3 
// 4 5 6 
// 7 8 9

2.3.3.7 for vs while

对比表格

特性for 循环while 循环
语法for(初始化; 条件; 更新){}while(条件){}
初始化在循环头部集中定义需要在循环外单独定义
更新自动执行更新语句需要在循环体中手动更新
循环次数通常确定,由计数器决定不确定,由条件决定
使用场景次数确定的循环条件复杂的循环
可读性结构紧凑,一目了然更灵活,但需要手动管理
推荐程度固定次数循环优先使用不确定次数循环优先使用

使用建议

javascript
// 使用 for:次数确定
for (let i = 0; i < 10; i++) {
  console.log(i)
}

// 使用 while:条件复杂或次数不确定
let condition = true
let attempts = 0
while (condition && attempts < 5) {
  // 复杂的逻辑
  attempts++
}

2.3.3.8 注意事项
重要注意事项
  1. 循环变量的作用域
javascript
// 推荐:使用 let 声明循环变量
for (let i = 0; i < 5; i++) {
  console.log(i)
}
// 循环结束后,i 不可访问

// 不推荐:使用 var 声明循环变量
for (var j = 0; j < 5; j++) {
  console.log(j)
}
// 循环结束后,j 仍然可以访问(值为5)
  1. 避免在循环体中修改数组长度
javascript
// 危险:在循环中删除元素
let arr = [1, 2, 3, 4, 5]
for (let i = 0; i < arr.length; i++) {
  if (arr[i] === 3) {
    arr.splice(i, 1)  // 删除元素后索引会变化
    i--  // 需要手动调整索引
  }
}

// 推荐:倒序遍历删除
let arr2 = [1, 2, 3, 4, 5]
for (let i = arr2.length - 1; i >= 0; i--) {
  if (arr2[i] === 3) {
    arr2.splice(i, 1)
  }
}
  1. 避免无限循环
javascript
// 错误示例:条件永远为 true
for (let i = 0; i < 5; ) {
  console.log(i)
  // 忘记更新 i,死循环
}

// 错误示例:更新方向错误
for (let i = 0; i < 5; i--) {
  console.log(i)
  // i 减小,永远满足 i < 5
}

// 正确示例
for (let i = 0; i < 5; i++) {
  console.log(i)
}
  1. 注意边界条件
javascript
// 数组遍历
let arr = [1, 2, 3, 4, 5]

// 正确
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i])  // 1, 2, 3, 4, 5
}

// 错误:越界
for (let i = 0; i <= arr.length; i++) {
  console.log(arr[i])  // 最后一次 arr[5] 是 undefined
}

2.3.3.9 面试题
javascript
// 面试题1:以下代码输出什么?
for (let i = 0; i < 3; i++) {
  console.log(i)
}
// 输出:
// 0
// 1
// 2

// 面试题2:以下代码循环多少次?
for (let i = 1; i <= 100; i += 2) {
  // 只处理奇数
}
// 循环次数:50次(1, 3, 5, ..., 99)

// 面试题3:以下代码输出什么?
for (let i = 0; i < 5; i++) {
  console.log('A')
  if (i === 2) break
}
// 输出:
// A
// A
// A
// 解释:i=2 时 break 退出循环

// 面试题4:以下代码输出什么?
for (let i = 0; i < 5; i++) {
  if (i % 2 === 0) continue
  console.log(i)
}
// 输出:
// 1
// 3
// 解释:偶数跳过,只输出奇数

// 面试题5:以下代码输出什么?
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
}
// 输出:
// 3
// 3
// 3
// 解释:setTimeout 是异步,循环结束时 i=3

// 使用闭包解决
for (let i = 0; i < 3; i++) {
  (function(i) {
    setTimeout(() => {
      console.log(i)
    }, 0)
  })(i)
}
// 输出:
// 0
// 1
// 2

// 面试题6:var 和 let 的区别
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log('var:', i)
  }, 0)
}
// 输出:3, 3, 3

for (let j = 0; j < 3; j++) {
  setTimeout(() => {
    console.log('let:', j)
  }, 0)
}
// 输出:0, 1, 2

// 面试题7:嵌套循环输出什么?
for (let i = 1; i <= 3; i++) {
  let str = ''
  for (let j = 1; j <= i; j++) {
    str += j
  }
  console.log(str)
}
// 输出:
// 1
// 12
// 123

// 面试题8:计算结果
let sum = 0
for (let i = 1; i <= 10; i++) {
  if (i % 2 === 0) continue
  sum += i
}
console.log(sum)
// 输出:25(1 + 3 + 5 + 7 + 9 = 25)

2.3.3.10 最佳实践

推荐做法

  1. 使用 let 声明循环变量
javascript
// 推荐
for (let i = 0; i < 10; i++) {
  // 代码
}
  1. 循环变量使用有意义的名称
javascript
// 推荐
for (let index = 0; index < users.length; index++) {
  console.log(users[index].name)
}

// 不推荐
for (let i = 0; i < users.length; i++) {
  console.log(users[i].name)
}
  1. 固定次数循环使用 for,不确定次数使用 while
javascript
// 推荐:次数固定
for (let i = 0; i < 10; i++) {
  // 代码
}

// 推荐:次数不确定
while (condition) {
  // 代码
}
  1. 使用 break 和 continue 控制循环
javascript
// 使用 break 退出循环
for (let i = 0; i < 100; i++) {
  if (foundTarget) break
}

// 使用 continue 跳过本次循环
for (let i = 0; i < 100; i++) {
  if (shouldSkip(i)) continue
  // 处理 i
}
  1. 倒序遍历删除数组元素
javascript
// 推荐:倒序删除
for (let i = arr.length - 1; i >= 0; i--) {
  if (shouldRemove(arr[i])) {
    arr.splice(i, 1)
  }
}

避免做法

  1. 避免在循环中修改数组长度
javascript
// 不推荐:正序删除元素
for (let i = 0; i < arr.length; i++) {
  arr.splice(i, 1)  // 索引会混乱
}
  1. 避免过深的嵌套
javascript
// 不推荐:嵌套过深
for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    for (let k = 0; k < 10; k++) {
      // 代码
    }
  }
}

// 推荐:拆分为函数
function processItem(i, j, k) {
  // 代码
}

for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    for (let k = 0; k < 10; k++) {
      processItem(i, j, k)
    }
  }
}
  1. 避免在循环体中进行复杂计算
javascript
// 不推荐:每次循环都计算
for (let i = 0; i < array.length; i++) {
  let result = expensiveCalculation(i)
  // 使用 result
}

// 推荐:缓存计算结果
const cachedResults = {}
for (let i = 0; i < array.length; i++) {
  if (!cachedResults[i]) {
    cachedResults[i] = expensiveCalculation(i)
  }
  // 使用 cachedResults[i]
}

2.3.3.11 总结

for 循环要点

  1. 语法for (初始化; 条件; 更新) { 循环体 }
  2. 执行:初始化 → 判断条件 → 执行循环体 → 更新 → 返回判断
  3. 特点:结构紧凑,初始化、条件、更新集中在一行
  4. 优势:适合固定次数的循环,代码清晰易读
  5. 场景:数组遍历、计数循环、嵌套循环
  6. 对比:比 while 更结构化,适合确定次数的循环
  7. 最佳实践:使用 let 声明变量、注意边界条件、避免死循环

核心要点

  • for 循环是 JavaScript 中最常用的循环结构
  • 初始化、条件、更新三个部分集中在循环头部
  • 适合循环次数确定的情况
  • 可以嵌套使用实现多重循环
  • 使用 let 避免变量提升问题

Released under the MIT License.