【JS基础-Day3】循环与数组
📺 对应视频:P35-P49 | 🎯 核心目标:掌握 for 循环、嵌套循环、数组的增删改查以及排序算法
一、for 循环
1.1 基本语法
// for (初始化; 条件; 迭代表达式) { 循环体 }
for (let i = 0; i < 5; i++) {
console.log(i) // 0 1 2 3 4
}
// 执行顺序:
// 1. let i = 0(只执行一次)
// 2. 检查 i < 5
// 3. 执行循环体
// 4. i++
// 5. 回到步骤2,直到条件为 false
1.2 for 循环的灵活写法
// 倒序循环
for (let i = 10; i >= 0; i--) {
console.log(i) // 10 9 8 ... 0
}
// 步长不为1
for (let i = 0; i <= 100; i += 5) {
console.log(i) // 0 5 10 15 ... 100
}
// 累加求和
let sum = 0
for (let i = 1; i <= 100; i++) {
sum += i
}
console.log(sum) // 5050(高斯求和)
// 累乘(阶乘)
let factorial = 1
for (let i = 1; i <= 5; i++) {
factorial *= i
}
console.log(factorial) // 120(5! = 120)
1.3 while vs for 的选择
for 循环:已知循环次数时用
while 循环:不知道循环几次、以条件为终止时用
// 例:读取文件直到结束(while 更合适)
while (!eof) { readLine() }
// 例:遍历1到10(for 更合适)
for (let i = 1; i <= 10; i++) { ... }
二、循环嵌套
2.1 基本概念
// 外层循环控制行,内层循环控制列
for (let i = 1; i <= 3; i++) { // 外层:3次
for (let j = 1; j <= 3; j++) { // 内层:每次3次,共9次
console.log(`i=${i}, j=${j}`)
}
}
2.2 经典应用:打印图形
// 打印直角三角形
for (let i = 1; i <= 5; i++) {
let row = ''
for (let j = 1; j <= i; j++) {
row += '★'
}
console.log(row)
}
// ★
// ★★
// ★★★
// ★★★★
// ★★★★★
// 打印九九乘法表
for (let i = 1; i <= 9; i++) {
let row = ''
for (let j = 1; j <= i; j++) {
row += `${j}×${i}=${i*j}\t`
}
console.log(row)
}
💡 嵌套循环时间复杂度:两层嵌套是 O(n²),三层是 O(n³),数据量大时要警惕性能问题。
三、数组基础
3.1 什么是数组?
数组是一种有序的数据集合,可以存储多个值。
// 创建数组
let arr1 = [1, 2, 3, 4, 5] // 字面量(推荐)
let arr2 = new Array(3) // [empty × 3],长度为3的空数组
let arr3 = new Array(1, 2, 3) // [1, 2, 3]
// 混合类型数组(JS 允许,但不推荐)
let mixed = [1, 'hello', true, null, {name: '张三'}, [1, 2]]
3.2 访问元素
let fruits = ['苹果', '香蕉', '橙子']
// 索引从 0 开始
console.log(fruits[0]) // '苹果'
console.log(fruits[1]) // '香蕉'
console.log(fruits[2]) // '橙子'
console.log(fruits[3]) // undefined(越界)
// 最后一个元素
console.log(fruits[fruits.length - 1]) // '橙子'
// 数组长度
console.log(fruits.length) // 3
3.3 遍历数组
let scores = [85, 92, 78, 95, 88]
// 方式一:for 循环(经典)
for (let i = 0; i < scores.length; i++) {
console.log(scores[i])
}
// 方式二:for...of(简洁,ES6)
for (let score of scores) {
console.log(score)
}
// 方式三:forEach(回调函数,进阶)
scores.forEach((score, index) => {
console.log(`第${index+1}个:${score}`)
})
四、数组的增删改查
4.1 增加元素
let arr = [1, 2, 3]
// 末尾添加(push)- 返回新长度
arr.push(4) // [1, 2, 3, 4]
arr.push(5, 6) // [1, 2, 3, 4, 5, 6]
// 头部添加(unshift)- 返回新长度,性能差
arr.unshift(0) // [0, 1, 2, 3, 4, 5, 6]
// 指定位置插入(splice)
arr.splice(2, 0, 'a', 'b') // 在索引2处插入'a','b'
// splice(开始索引, 删除数量, ...插入元素)
4.2 删除元素
let arr = [1, 2, 3, 4, 5]
// 删除末尾(pop)- 返回被删除的元素
let last = arr.pop() // last=5, arr=[1,2,3,4]
// 删除头部(shift)- 返回被删除的元素,性能差
let first = arr.shift() // first=1, arr=[2,3,4]
// 删除指定位置(splice)
arr.splice(1, 2) // 从索引1开始删2个 → arr=[2]
arr.splice(1, 1) // 删除索引1的元素
4.3 修改元素
let arr = ['a', 'b', 'c']
// 直接索引修改
arr[1] = 'B' // ['a', 'B', 'c']
// splice 替换
arr.splice(0, 1, 'A') // 删除索引0,插入'A' → ['A', 'B', 'c']
4.4 查找元素
let arr = [10, 20, 30, 20, 40]
// indexOf:找第一个匹配的索引,没找到返回 -1
arr.indexOf(20) // 1
arr.indexOf(99) // -1
// lastIndexOf:从末尾开始找
arr.lastIndexOf(20) // 3
// includes:是否包含(返回布尔值)
arr.includes(30) // true
arr.includes(99) // false
// find:找到第一个满足条件的元素(ES6)
arr.find(item => item > 15) // 20
// findIndex:找到第一个满足条件的索引(ES6)
arr.findIndex(item => item > 25) // 2(值30在索引2)
五、数组常用方法
5.1 截取与合并
let arr = [1, 2, 3, 4, 5]
// slice:截取(不修改原数组)
arr.slice(1, 3) // [2, 3](包含start,不含end)
arr.slice(2) // [3, 4, 5]
arr.slice(-2) // [4, 5](负数从末尾数)
// concat:合并数组(不修改原数组)
let a = [1, 2]
let b = [3, 4]
let c = a.concat(b) // [1, 2, 3, 4]
let d = [...a, ...b] // [1, 2, 3, 4](展开运算符,更常用)
5.2 转换方法
// join:数组转字符串
[1, 2, 3].join() // '1,2,3'(默认逗号)
[1, 2, 3].join('-') // '1-2-3'
[1, 2, 3].join('') // '123'
// split:字符串转数组(与 join 相反)
'a,b,c'.split(',') // ['a', 'b', 'c']
'hello'.split('') // ['h', 'e', 'l', 'l', 'o']
5.3 排序方法
let arr = [3, 1, 4, 1, 5, 9, 2, 6]
// reverse:反转数组(修改原数组)
arr.reverse() // [6, 2, 9, 5, 1, 4, 1, 3]
// sort:排序(修改原数组)
// ⚠️ 默认按字符串Unicode排序,数字排序需要传比较函数!
[10, 9, 2].sort() // [10, 2, 9](字符串排序,错误!)
[10, 9, 2].sort((a, b) => a - b) // [2, 9, 10](升序)
[10, 9, 2].sort((a, b) => b - a) // [10, 9, 2](降序)
六、冒泡排序
6.1 算法思路
通过相邻元素比较和交换,每轮把最大值"冒泡"到末尾。
第1轮:[5,3,1,4,2] → 比较并交换 → [3,1,4,2,5](5冒到最后)
第2轮:[3,1,4,2,5] → 比较并交换 → [1,3,2,4,5](4冒到倒数第2)
...
6.2 代码实现
function bubbleSort(arr) {
let len = arr.length
for (let i = 0; i < len - 1; i++) { // 外层:控制轮数
for (let j = 0; j < len - 1 - i; j++) { // 内层:控制比较次数
if (arr[j] > arr[j + 1]) {
// 交换相邻元素
let temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
// ES6 解构交换写法:[arr[j], arr[j+1]] = [arr[j+1], arr[j]]
}
}
}
return arr
}
console.log(bubbleSort([5, 3, 1, 4, 2])) // [1, 2, 3, 4, 5]
时间复杂度分析:
- 最坏情况(逆序):O(n²)
- 最好情况(已排序):O(n)(加了优化后)
- 空间复杂度:O(1)
6.3 优化版(提前终止)
function bubbleSortOptimized(arr) {
let len = arr.length
for (let i = 0; i < len - 1; i++) {
let swapped = false // 本轮是否发生了交换
for (let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
;[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
swapped = true
}
}
if (!swapped) break // 没有发生交换,说明已经有序
}
return arr
}
七、综合练习
练习1:求数组最大值
function getMax(arr) {
let max = arr[0]
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i]
}
return max
}
// 或者使用内置方法:
Math.max(...[3, 1, 4, 1, 5]) // 5
练习2:数组去重
// 方法一:Set(ES6,最简洁)
let unique = [...new Set([1, 2, 2, 3, 3, 4])] // [1, 2, 3, 4]
// 方法二:filter + indexOf
let arr = [1, 2, 2, 3, 3, 4]
let unique2 = arr.filter((item, index) => arr.indexOf(item) === index)
练习3:找出所有偶数
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
let evens = numbers.filter(n => n % 2 === 0) // [2, 4, 6, 8]
八、知识图谱
循环与数组
├── for 循环
│ ├── 基本语法(初始化、条件、迭代)
│ ├── 各种变体(倒序、步长)
│ └── 嵌套循环(打印图形、九九表)
├── 数组
│ ├── 创建:字面量 [] / new Array()
│ ├── 访问:arr[index],长度 .length
│ ├── 遍历:for / for...of / forEach
│ ├── 增:push / unshift / splice
│ ├── 删:pop / shift / splice
│ ├── 改:直接赋值 / splice
│ ├── 查:indexOf / includes / find
│ └── 常用方法:slice / join / sort / reverse
└── 排序算法
└── 冒泡排序(相邻比较交换,O(n²))
九、高频面试题
Q1: splice 和 slice 的区别?
splice会修改原数组,可以增删改;slice不修改原数组,只是截取返回新数组。
Q2:如何判断一个值是数组?
Array.isArray([]) // true,推荐
[] instanceof Array // true,也可以
Q3:数组的 sort 为什么需要传比较函数?
默认的
sort把元素转成字符串再比较 Unicode,导致[10, 9, 2].sort()得到[10, 2, 9](字符串 "10" < "2")。传入(a, b) => a - b才能正确数值排序。
⬅️ 上一篇:Day2 - 运算符与分支 ➡️ 下一篇:Day4 - 函数与作用域