Skip to content

1-9for循环嵌套案例 ​

本页对应 第一章JavaScript基础,共 5 段视频,约 28 分钟。

学习目标 ​

  • 能用自己的话说明循环嵌套的结构,并举例解释“外层循环执行一次,内层循环执行全部次数”的执行特点。
  • 能独立使用双重 for 循环打印 5 行 5 列小星星,并将换行语句放在内层循环结束后。
  • 能根据“每行星星数等于当前行号”的规律,编写双重循环打印 5 行三角形星星,并说明 j <= i 的作用。
  • 能将用户输入的行数、列数转换为数字,并通过双重循环打印对应行数和列数的小星星。
  • 能使用双重循环和模板字符串输出 99 乘法表,正确使用列数与行数生成“列 × 行 = 积”的表达式。
  • 能用 span 包裹乘法表表达式,并通过 CSS 设置显示方式、边框、间距、圆角、颜色和阴影完成美化。

知识点 ​

数组排序sort:为什么需要 ​

实际开发里经常要把一组数字按大小排列,比如成绩、价格、销量。手写排序麻烦,数组自带的 sort() 可以直接排序。本节笔记先演示默认排序,再演示升序、降序,以及排序后取最大值和最小值。

数组排序sort:怎么想 ​

sort() 会修改原数组,所以调用后 arr 本身变了。默认不传比较函数时,它按字符串规则比较,因此 [88, 78, 100, 34, 99] 排成 [100, 34, 78, 88, 99],并不是数值升序。要按数值排,就传比较函数:升序返回 a - b,降序返回 b - a。排好序后,降序数组的第一个是最大值,最后一个是 minced 最小值;笔记里正是先降序,再取 arr[0] 和 arr[arr.length - 1]。

数组排序sort:怎么做 ​

保留笔记代码如下:

javascript
<script>
    // 数组排序  数组.sort()  修改原数组
    let arr = [88, 78, 100, 34, 99]
    // arr.sort()
    // console.log(arr)  // [100, 34, 78, 88, 99]

    // 1. 升序排序
    // arr.sort(function (a, b) {
    //   return a - b
    // })
    // console.log(arr)  // [34, 78, 88, 99, 100]

    // 2. 降序排序
    arr.sort(function (a, b) {
      return b - a
    })
    console.log(arr)  //  [100, 99, 88, 78, 34]

    // 3. 获取数组最大值和最小值
    document.write(`数组的最大值:\${arr[0]}`)
    document.write(`数组的最小值:\${arr[arr.length - 1]}`)
  </script>

数组排序sort:易错点 ​

第一,别把默认 sort() 当成数值排序;第二,比较函数一定要有返回值,升序写 return a - b,降序写 return b - a;第三,sort() 修改原数组,若后面还要用原顺序,要先复制;第四,取最大值和最小值前要明确当前是升序还是降序,降序时首元素最大、尾元素最小。

选择排序:为什么需要 ​

内置 sort() 很方便,但学习选择排序能理解“排序到底怎么发生”,也能训练循环嵌套、索引记录和变量交换。笔记把它标为“了解”,重点是理解原理,不要求替代 sort()。

选择排序:怎么想 ​

选择排序的思路是:从第一个位置开始,在它和后面所有数里找最小数,放到第一个位置;第二轮从第二个位置开始找剩余最小数,放到第二个位置;以此类推。外层循环控制“轮数”,内层循环控制“查找最小值”。转写稿里用打印星星演示过嵌套循环:外层每执行一次,内层要执行完整次数;这里同样如此,外层每轮确定一个位置,内层扫描剩余元素。转写稿中的“否循环/风循环”应纠正为 for 循环,“债”是变量 j。算法可视化可参考:https://visualgo.net/zh/sorting 。

选择排序:怎么做 ​

保留笔记代码如下:

javascript
<script>
  // 选择排序算法
  let arr = [4, 2, 5, 1, 3]
// 1  [1, 2, 5, 4, 3]
// 2  [1, 2, 5, 4, 3]
// 3  [1, 2, 3, 4, 5]
// 4  [1, 2, 3, 4, 5]
// 1. 外层循环控制是几轮能够比较出来
for (let i = 0; i < arr.length - 1; i++) {
  // 最小值的索引号
  let minIndex = i

  // 2. 里层循环来查找最小值,并且记录最小值的索引号
  for (let j = i + 1; j < arr.length; j++) {
    if (arr[j] < arr[minIndex]) {
      minIndex = j
    }
  }

  // 3. 交换变量  
  if (minIndex !== i) {
    let temp = arr[minIndex]
    arr[minIndex] = arr[i]
    arr[i] = temp
  }
}
console.log(arr)

选择排序:易错点 ​

外层循环次数是 arr.length - 1,因为最后一个位置不用再比较;内层必须从 i + 1 开始,避免和自己比较;每轮开始都要把 minIndex 重置为 i;只有 minIndex !== i 时才交换;交换要用临时变量 temp,否则值会被覆盖。调试时可看笔记注释:第一轮把最小值 1 换到前面,后面几轮逐步把 2、3、4 放到对应位置。

课件图示 ​

课件第43页:拓展 - 数组排序 l 数组. sort() 方法可以排序 l 语法: l 数组

课件第2页:今日单词 模块 说明 单词 作用 数组 数组长度 数组.length 得到数组的

课件第7页:l 循环嵌套: 一个循环语句里面又包含另一个循环语句 1. 循环嵌套 for(初

课件第9页:练习 • 打印5行5列的星星 需求: 页面中打印出5行5列的星星 分析: ①:利

课件第10页:练习 • 打印三角形星星 需求:如图所示 分析: ①:利用双重for循环来做 ②

课件第11页:练习 • 九九乘法表 需求:如图所示 分析: ①:只需要把刚才三角形星星做改动

课件第14页:2. 数组(Array) - 重点 数组:(Array) 是一种数据类型,属于引

课件第15页:1. 声明语法 l 注意事项: Ø 数组是按顺序保存(有序),所以每个数据都有自

代码示例 ​

示例一:循环嵌套 —— 打印 N 行 N 列小星星 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>循环嵌套 —— 打印 N 行 N 列小星星</title>
</head>
<body>
  <script>
    // 用户输入行数和列数(+ 号把字符串转成数字,方便比较)
    let rows = +prompt('请输入行数')
    let cols = +prompt('请输入列数')

    // 外层循环:控制打印几行
    for (let i = 1; i <= rows; i++) {

      // 内层循环:控制每行打印几颗
      for (let j = 1; j <= cols; j++) {
        document.write('☆')
      }

      // 一行全部打印完毕,再换行
      document.write('<br>')
    }
  </script>
</body>
</html>

关键行与易错点

  • for (let i = 1; i <= rows; i++) 是外层循环,控制行数;for (let j = 1; j <= cols; j++) 是内层循环,控制每行打印几个。外层变量习惯用 i(index),内层用 j。
  • document.write('<br>') 必须写在内层 for 循环的外面、外层循环的里面。如果写进内层循环,就会变成“打印一颗星星就换一次行”,所有星星竖着排成一条线。
  • 循环嵌套的执行规律:外层循环每执行一次,内层循环要把自己的全部次数跑完。所以行数为 3 时,内层这套“打 5 颗星 + 换行”的流程会被完整执行 3 遍。
  • prompt 拿到的是字符串,用 +prompt(...) 转成数字再和 i 做 <= 比较,否则容易出现意外的比较结果。
  • 想要实心星星,可以打开输入法软键盘的“特殊符号”面板选实心五角星。

示例二:循环嵌套 —— 打印三角形星星 + 99 乘法表 ​

js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>循环嵌套 —— 三角形星星 与 99 乘法表</title>
  <style>
    span {
      display: inline-block;      /* 让 span 变成一行内可排列的盒子 */
      width: 80px;
      border: 1px solid pink;
      text-align: center;         /* 文字水平居中 */
      padding: 5px;
      margin: 2px;
      border-radius: 5px;         /* 小圆角 */
      color: hotpink;
      background: rgba(255, 192, 203, .2);  /* 半透明粉色背景 */
      box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);  /* 小影子 */
    }
  </style>
</head>
<body>
  <script>
    // ===== 1. 三角形星星 =====
    for (let i = 1; i <= 5; i++) {        // 外层:行数,固定 5 行
      for (let j = 1; j <= i; j++) {      // 内层:每行个数 = 行数
        document.write('☆')
      }
      document.write('<br>')
    }

    document.write('<hr>')

    // ===== 2. 99 乘法表 =====
    for (let i = 1; i <= 9; i++) {        // 外层:行,1~9
      for (let j = 1; j <= i; j++) {      // 内层:列,列数 = 当前行号
        document.write(`<span>${j} × ${i} = ${i * j}</span>`)
      }
      document.write('<br>')
    }
  </script>
</body>
</html>

关键行与易错点

  • 三角形星星的核心是 j <= i:每行打印的个数等于当前行号(第 1 行 1 颗、第 5 行 5 颗)。这里如果写成 j <= 5 就退化成矩形了。
  • 99 乘法表就是把三角形里的星星换成公式:\${j} × \${i} = \${i * j}。前面的数用列 j,后面的数用行 i,因为 2×3 出现在第 2 列、第 3 行;结果是 i * j,不要写死。
  • 模板字符串的反引号不要写成单引号,否则 ${} 不会被解析。
  • <br> 依旧写在内层循环之后,保证一整行公式打完再换行。
  • 美化时 display: inline-block 是让 span 盒子在一行内横向排列的关键;width 固定后列才能对齐。半透明背景用 rgba() 第四位小数控制透明度。

示例三:数组排序 sort 与选择排序 ​

js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>数组排序 sort 与选择排序</title>
</head>
<body>
  <script>
    // ========== 1. sort 排序(会修改原数组) ==========
    let arr = [88, 78, 100, 34, 99]

    // 不传参:默认按字符串排序,结果是 [100, 34, 78, 88, 99]
    // arr.sort()

    // 升序
    arr.sort(function (a, b) {
      return a - b
    })
    console.log(arr)  // [34, 78, 88, 99, 100]

    // 降序
    arr.sort(function (a, b) {
      return b - a
    })
    console.log(arr)  // [100, 99, 88, 78, 34]

    // 降序后:第一个是最大值,最后一个是能最小值
    document.write(`数组的最大值:${arr[0]}<br>`)
    document.write(`数组的最小值:${arr[arr.length - 1]}<br>`)

    // ========== 2. 选择排序 ==========
    let nums = [4, 2, 5, 1, 3]

    // 外层循环:一共比较 length - 1 轮
    for (let i = 0; i < nums.length - 1; i++) {
      let minIndex = i   // 假设本轮最小值的索引是 i

      // 内层循环:从 i + 1 开始找更小的数
      for (let j = i + 1; j < nums.length; j++) {
        if (nums[j] < nums[minIndex]) {
          minIndex = j   // 记录最小值所在的索引
        }
      }

      // 如果最小值不在 i 位置,就交换
      if (minIndex !== i) {
        let temp = nums[minIndex]
        nums[minIndex] = nums[i]
        nums[i] = temp
      }
    }
    console.log(nums)  // [1, 2, 3, 4, 5]
  </script>
</body>
</html>

关键行与易错点

  • arr.sort() 不传参数时是按字符串规则排序,所以 100 会排在 34 前面,必须传比较函数。
  • 比较函数 return a - b 是升序,return b - a 是降序。sort() 会修改原数组,升序排完再排降序,操作的是同一个数组。
  • 求最大值最小值要看清排序方向:降序后才是 arr[0] 最大、arr[arr.length - 1] 最小;如果是升序,则正好相反。
  • 选择排序外层是 i < arr.length - 1(最后一轮只剩一个数,不用再比);内层从 j = i + 1 开始,避免自己和自己比较。
  • minIndex 一开始必须赋值成 i,而不是 0,否则前面已经排好的位置会被破坏。
  • 交换变量必须借助临时变量 temp,写成 a = b; b = a 会丢掉数据;if (minIndex !== i) 只是优化,索引相同时可以不交换。

常见错误 ​

  1. arr.sort() 直接排数字,结果变成 [100, 34, 78, 88, 99] 现象:数字数组排序后顺序诡异,100 排到了最前面。 原因:不传比较函数时,sort() 把所有元素转成字符串按 Unicode 码位比较,"100" < "34"。 修法:数字排序必须传比较函数,升序 (a, b) => a - b,降序 (a, b) => b - a。

  2. 以为 sort() 返回一个新数组 现象:let newArr = arr.sort(...) 之后改 newArr,发现原 arr 也跟着变了。 原因:sort() 原地修改数组,返回值就是原数组本身的引用。 修法:需要保留原数组时先 arr.slice() 复制一份再排序。

  3. 星星 / 乘法表全部竖着排成一列 现象:想打印 5 行 5 列,结果打印出 25 行 1 列。 原因:<br> 写在了内层循环体内,每打印一个元素就换一次行。 修法:<br> 必须放在内层 for 的右花括号之后、外层循环体内,即"一行全部打印完再换行"。

  4. 三角形星星每行都是 5 颗 现象:内层条件照抄成 j <= 5,打出来是矩形而不是三角形。 原因:没有把"每行个数"和"当前行号"建立联系,内层条件写成了固定值。 修法:内层条件用外层变量,写成 j <= i,让第 i 行恰好打印 i 颗。

  5. 内外层循环变量都叫 i 现象:三角形 / 乘法表输出错乱,甚至页面卡死。 原因:内层 let i 遮蔽了外层 i,外层循环的推进被破坏。 修法:约定外层用 i,内层用 j(再深一层用 k)。

  6. 选择排序里 minIndex 定义位置不对或交换不用临时变量 现象一:排序结果部分正确部分错乱;现象二:两个元素交换后变成同一个值。 原因:minIndex 写在了外层循环外面,每轮不重置;交换时直接 a = b; b = a。 修法:let minIndex = i 必须放在外层循环体内第一行;交换必须借助 temp(或解构 [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]])。

练习 ​

  1. 倒三角星星:打印 5 行,第 1 行 5 颗、第 5 行 1 颗。写出内层循环的条件表达式,并说明它和第 4 条易错点的区别。

  2. 成绩排行榜:给定 [88, 78, 100, 34, 99],用 sort 降序取出前三名,要求原数组保持不变。写出两种实现(先复制再排 / 用 toSorted),并说明为什么直接 arr.sort() 会出问题。

  3. 选择排序改降序:把笔记中的选择排序改成从大到小,并额外统计一共发生了多少次交换,最后打印交换次数。

  4. 健壮的 N 行 M 列星星:用户通过 prompt 输入行数和列数,要求:输入 0、负数或非数字时给出提示并中止打印;输入合法时正常输出。思考 prompt 应该写在哪一层,为什么不能写进循环里。

  5. 99 乘法表变形:在已完成的下三角乘法表基础上,只打印"行号为偶数"的行(第 2、4、6、8 行),并让每行的乘积结果单独用 <span> 加粗。提示:改动只发生在外层循环的步长上。

原视频对照 ​

#视频时长
1今日内容01:46
2循环嵌套05:35
3打印5行5列的小星星07:54
4打印三角形星星06:48
599乘法表06:44

小结 ​

  1. 循环嵌套的执行模型是"外层走一步,内层跑完整轮",判断循环次数永远用乘法而不是加法。
  2. 打印二维图形类问题的通用公式:外层管行数,内层管列数,换行放在两层之间——这三句话能覆盖星星、三角形、乘法表全部变体。
  3. 图形题从"矩形"到"三角形"的唯一变化点,是把内层的固定上界换成与外层变量挂钩的表达式;会做矩形就应该会做三角形。
  4. sort() 是原地方法、默认按字符串比较,这两点是它绝大多数 bug 的来源;数字排序时比较函数不是可选项而是必选项。
  5. 选择排序的三个必要件是"外层轮数 length - 1""每轮重置 minIndex""用临时变量交换",缺一个结果就会出错。
  6. 手写选择排序的价值不在排序本身(实际项目一律用 sort),而在于它是"循环嵌套 + 索引操作 + 变量交换"的最小综合训练场。
  7. 任何"一行/一组内容结束后才该做的事"(换行、分隔符、求和归零)都应放在对应层循环的外面,这是循环边界感的核心。
  8. 从今天起建立变量命名直觉:i 行、j 列、k 第三层,minIndex 存索引,命名对得上语义,嵌套层级就不会写乱。