10.4 第三种:右下三角(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>九九乘法表 - 第三种(右下三角)</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); min-height: 100vh; }
h2 { color: white; text-align: center; }
table { border-collapse: collapse; table-layout: fixed; width: 900px; margin: 0 auto; }
td { padding: 8px 12px; text-align: center; font-size: 0.85em; white-space: nowrap; border-radius: 4px; }
td:not(:empty) { border: 1px solid rgba(255,255,255,0.4); background: rgba(255,255,255,0.15); color: white; }
td:not(:empty):hover { background: rgba(255,255,255,0.35); transition: 0.2s; }
</style>
</head>
<body>
<h2>九九乘法表 · 第三种(右下三角)</h2>
<script>
document.write('<table><tbody>');
// 【外层循环】控制行数
for (var i = 1; i <= 9; i++) {
document.write('<tr>');
// 【第一个内层循环】输出空单元格(占位)
// 第1行:9-1=8个空格,第2行:9-2=7个空格...
// 作用:把有内容的单元格"推"到右边
for (var j = 1; j <= 9 - i; j++) {
document.write('<td></td>'); // 空单元格(无内容)
}
// 【第二个内层循环】输出有内容的单元格
// 和第一种相同,第i行输出i列
for (var j = 1; j <= i; j++) {
document.write('<td>' + j + '×' + i + '=' + (j * i) + '</td>');
}
document.write('</tr>');
}
document.write('</tbody></table>');
</script>
</body>
</html>

📝 右对齐的核心技巧
空格数量计算规律:
js
总列数 = 9(固定)
第 i 行有内容的列数 = i
第 i 行空格数 = 9 - i
示例:
第1行:9-1=8个空格 + 1个内容 → 右对齐
第2行:9-2=7个空格 + 2个内容
第3行:9-3=6个空格 + 3个内容
...
第9行:9-9=0个空格 + 9个内容 → 填满整行
执行流程可视化:
第1行(i=1):
循环1: 输出8个空格(j: 1→8)
循环2: 输出1个内容(j: 1→1)→ 1×1=1
第2行(i=2):
循环1: 输出7个空格(j: 1→7)
循环2: 输出2个内容(j: 1→2)→ 1×2=2, 2×2=4
第3行(i=3):
循环1: 输出6个空格(j: 1→6)
循环2: 输出3个内容(j: 1→3)→ 1×3=3, 2×3=6, 3×3=9
CSS 技巧:
css
td:not(:empty) {
/* 只给非空单元格添加样式 */
border: 1px solid rgba(255,255,255,0.4);
background: rgba(255,255,255,0.15);
}
- 作用: 空单元格不显示边框和背景,实现"透明占位"效果
- 伪类
:not(:empty):选择所有不为空的<td>元素
🎯 知识点提炼
一行两个循环的模式:
模式:前补空格 + 后输出内容
应用:表格右对齐、金字塔图案、层次缩进
空间补偿思想:
固定总宽度(9列)
实际内容宽度(i列)
补偿空白宽度(9-i列)
→ 实现右对齐视觉效果
与第一种的对比:
| 维度 | 第一种(左下) | 第三种(右下) |
|---|---|---|
| 外层循环 | i: 1→9 |
i: 1→9(相同) |
| 内层循环数量 | 1 个 | 2 个 |
| 空格处理 | 无 | 前补 9-i 个 |
| 视觉效果 | 左对齐 | 右对齐 |
| 单元格总数 | 45 个 | 45+36=81 个(含空格) |
10.5 第四种:右上三角(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>九九乘法表 - 第四种(右上三角)</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); min-height: 100vh; }
h2 { color: white; text-align: center; }
table { border-collapse: collapse; table-layout: fixed; width: 900px; margin: 0 auto; }
td { padding: 8px 12px; text-align: center; font-size: 0.85em; white-space: nowrap; border-radius: 4px; }
td:not(:empty) { border: 1px solid rgba(255,255,255,0.4); background: rgba(255,255,255,0.15); color: white; }
td:not(:empty):hover { background: rgba(255,255,255,0.35); transition: 0.2s; }
</style>
</head>
<body>
<h2>九九乘法表 · 第四种(右上三角)</h2>
<script>
document.write('<table><tbody>');
// 外层倒序:i 从 9 到 1
for (var i = 9; i >= 1; i--) {
document.write('<tr>');
// 前补 9-i 个空单元格
for (var j = 1; j <= 9 - i; j++) {
document.write('<td></td>');
}
// 输出有内容的单元格
for (var j = 1; j <= i; j++) {
document.write('<td>' + j + '×' + i + '=' + (j * i) + '</td>');
}
document.write('</tr>');
}
document.write('</tbody></table>');
</script>
</body>
</html>

📝 代码解释:第四种(右上三角)- 倒序 + 前补空格
第四种 = 第二种(左上)+ 空格偏移
js
// 第三种:右下三角(正序 + 前补空格)
for (var i = 1; i <= 9; i++) { // 正序 i: 1→9
for (var j = 1; j <= 9 - i; j++) {
document.write('<td></td>'); // 补空格:9-i 个
}
for (var j = 1; j <= i; j++) {
document.write(/* 内容 */); // 内容:i 个
}
}
// 第四种:右上三角(倒序 + 前补空格)
for (var i = 9; i >= 1; i--) { // 倒序 i: 9→1
for (var j = 1; j <= 9 - i; j++) {
document.write('<td></td>'); // 补空格:9-i 个(第1行i=9→0个空格)
}
for (var j = 1; j <= i; j++) {
document.write(/* 内容 */); // 内容:i 个(第1行i=9→9个内容)
}
}
第四种空格数量分析(倒序遍历):
i=9(第1行):空格 = 9-9=0,内容=9个 → 右对齐(但第1行本身满格,不偏移)
i=8(第2行):空格 = 9-8=1,内容=8个
i=7(第3行):空格 = 9-7=2,内容=7个
i=6(第4行):空格 = 9-6=3,内容=6个
...
i=1(第9行):空格 = 9-1=8,内容=1个 → 最右边1个,前补8个空格
视觉效果:
第三种(右下三角): 第四种(右上三角):
1×1=1 1×9=9 2×9=18 ... 9×9=81
1×2=2 2×2=4 1×8=8 ... 8×8=64
... ...
1×9=9 ... 9×9=81 1×1=1
四种形式总览对比:
| 形式 | 外层方向 | 空格位置 | 顶部 | 底部 |
|---|---|---|---|---|
| 第一种(左下) | 正序 1→9 | 无空格 | 少(1个) | 多(9个) |
| 第二种(左上) | 倒序 9→1 | 无空格 | 多(9个) | 少(1个) |
| 第三种(右下) | 正序 1→9 | 前补 9-i 个 | 少+靠右 | 多+靠左 |
| 第四种(右上) | 倒序 9→1 | 前补 9-i 个 | 多+靠右 | 少+靠右 |
四种形式的生成规律:
内容部分(所有形式相同):每行第i轮输出 i 个乘法式
内层 j: 1→i
变化点:
① 顶多底少 vs 顶少底多:外层 9→1(倒序) vs 1→9(正序)
② 左对齐 vs 右对齐: 有无前置空格(9-i 个)
10.6 九九乘法表知识点提炼
渲染错误: Mermaid 渲染失败: Parse error on line 12: ... td:not(:empty) 有内容 9-i 个空格 = -----------------------^ Expecting 'SPACELINE', 'NL', 'EOF', got 'SPACELIST'
11 跳转语句:break 与 continue
11.1 break 语句
作用: 结束整个循环(或 switch-case)
true
true
false
false
开始
i = 0
i <= 10?
i === 5?
结束整个循环
输出 i
i++
11.2 continue 语句
作用: 跳过本次循环的剩余语句,进入下一次循环
说明: mindmap 语法在部分 Markdown 预览器(旧版 Mermaid、部分 IDE)中不渲染或报错 。下面改用 flowchart + 子图 ,兼容性更好;并附表格作纯文本备用。
九九乘法表 知识点
外层循环:控制行数;正序 1→9 底行最宽,或倒序 9→1 顶行最宽
内层循环:j 从 1 到 i,每行输出 i 个乘法式
右对齐:每行先输出 9 减 i 个空单元格,再输出算式
样式:仅非空单元格加边框(如 CSS 选择器 not-empty)
四种形态:左下① 左上② 右下③ 右上④
知识点速查表(不依赖图表也能读):
| 模块 | 要点 |
|---|---|
| 外层循环 | 控制行;1→9 正序则底行最宽,9→1 倒序则顶行最宽 |
| 内层循环 | j 从 1 到 i,每行输出 i 个乘法式 |
| 空格 | 先输出 9 - i 个空 <td>,再输出算式,实现右对齐三角 |
| 样式 | 常用 td:not(:empty) 只给有内容的单元格加边框,空占位格无框 |
| 四种形态 | ①左下 ②左上(外层倒序)③右下(加空格)④右上(倒序+空格) |
11.3 break 与 continue 完整对比案例(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>break 与 continue 对比</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.container { display: flex; gap: 20px; flex-wrap: wrap; }
.card { flex: 1; min-width: 280px; background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.card h3 { margin-top: 0; }
.break-card h3 { color: #f44336; }
.continue-card h3 { color: #FF9800; }
.nums { display: flex; flex-wrap: wrap; gap: 8px; margin: 15px 0; }
.num { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border-radius: 50%; font-weight: bold; font-size: 1.1em; }
.printed { background: #4CAF50; color: white; }
.skipped { background: #FFF3E0; color: #FF9800; border: 2px dashed #FF9800; }
.stopped { background: #FFEBEE; color: #f44336; border: 2px dashed #f44336; }
.description { background: #f9f9f9; border-radius: 6px; padding: 12px; font-size: 0.9em; line-height: 1.6; }
.code { background: #1e1e1e; color: #d4d4d4; padding: 12px; border-radius: 6px; font-family: monospace; font-size: 0.85em; margin-top: 10px; }
.legend { display: flex; gap: 10px; margin-top: 10px; font-size: 0.85em; }
.legend-item { display: flex; align-items: center; gap: 5px; }
.dot { width: 16px; height: 16px; border-radius: 50%; }
</style>
</head>
<body>
<div class="container">
<div class="card continue-card">
<h3>continue:跳过第 5 次,其他正常输出</h3>
<div class="description">
遇到 i===5 时,跳过本次循环后面的语句,<strong>直接进入下一次循环</strong>。循环不会终止。
</div>
<div class="nums" id="continueNums"></div>
<div class="legend">
<div class="legend-item"><div class="dot" style="background:#4CAF50"></div> 正常输出</div>
<div class="legend-item"><div class="dot" style="background:#FFF3E0;border:2px dashed #FF9800"></div> 被跳过</div>
</div>
<div class="code">
for (var i = 0; i <= 10; i++) {<br>
if (i === 5) continue;<br>
console.log(i); // 输出:0,1,2,3,4,6,7,8,9,10<br>
}
</div>
</div>
<div class="card break-card">
<h3>break:遇到第 5 个立即终止</h3>
<div class="description">
遇到 i===5 时,立即终止整个循环,<strong>后面的所有迭代都不会执行</strong>。
</div>
<div class="nums" id="breakNums"></div>
<div class="legend">
<div class="legend-item"><div class="dot" style="background:#4CAF50"></div> 正常输出</div>
<div class="legend-item"><div class="dot" style="background:#FFEBEE;border:2px dashed #f44336"></div> 被终止</div>
</div>
<div class="code">
for (var i = 0; i <= 10; i++) {<br>
if (i === 5) break;<br>
console.log(i); // 输出:0,1,2,3,4<br>
}
</div>
</div>
</div>
<script>
// 演示 continue
var continueContainer = document.getElementById('continueNums');
for (var i = 0; i <= 10; i++) {
var div = document.createElement('div');
div.className = 'num';
div.textContent = i;
if (i === 5) {
div.classList.add('skipped');
div.title = '被 continue 跳过';
} else {
div.classList.add('printed');
}
continueContainer.appendChild(div);
}
// 演示 break
var breakContainer = document.getElementById('breakNums');
for (var i = 0; i <= 10; i++) {
var div = document.createElement('div');
div.className = 'num';
div.textContent = i;
if (i >= 5) {
div.classList.add('stopped');
div.title = i === 5 ? '触发 break,循环终止' : '未被执行';
} else {
div.classList.add('printed');
}
breakContainer.appendChild(div);
}
</script>
</body>
</html>

📝 代码解释:break 与 continue 核心区别
两个核心概念:
js
// continue:跳过本次,继续下次
for (var i = 0; i <= 10; i++) {
if (i === 5) continue; // 跳过 i=5,但循环继续
console.log(i); // 输出:0,1,2,3,4,6,7,8,9,10(少了5)
}
// break:立即结束整个循环
for (var i = 0; i <= 10; i++) {
if (i === 5) break; // i=5时,整个循环终止
console.log(i); // 输出:0,1,2,3,4(到4就停了)
}
continue 执行流程(i===5 时):
正常迭代:
i++ → 判断 i<=10 → 执行循环体 → 执行 console.log(i)
continue 迭代(i=5):
遇到 continue →【跳过】console.log(5) → 直接 i++ → 判断 i<=10 → 继续
break 执行流程(i===5 时):
正常迭代:
i++ → 判断 i<=10 → 执行循环体 → 执行 console.log(i)
break 迭代(i=5):
遇到 break →【立即退出循环】→ 后续 6,7,8,9,10 都不执行
DOM 可视化代码解析:
js
// continue 演示:始终创建所有圆圈,但 i=5 标记为"跳过"
for (var i = 0; i <= 10; i++) {
var div = document.createElement('div');
div.textContent = i;
if (i === 5) {
div.classList.add('skipped'); // 橙色虚线圆(被跳过)
div.title = '被 continue 跳过'; // 鼠标悬停提示
} else {
div.classList.add('printed'); // 绿色实心圆(正常输出)
}
continueContainer.appendChild(div);
}
// 注意:所有 11 个圆圈都创建了,只是 5 的样式不同
// break 演示:i>=5 都标记为"未执行"
for (var i = 0; i <= 10; i++) {
var div = document.createElement('div');
div.textContent = i;
if (i >= 5) {
div.classList.add('stopped'); // 红色虚线圆(被终止)
div.title = i === 5 ? '触发 break,循环终止' : '未被执行';
} else {
div.classList.add('printed'); // 绿色实心圆(已执行)
}
breakContainer.appendChild(div);
}
// 注意:这里用演示目的创建了所有圆圈,实际代码中 break 后面的迭代不会运行
⚠️ 常见混淆点:
js
// ❌ 误区1:continue 会跳到循环结束
// ✅ 正确:continue 只跳过本次的"剩余语句",循环继续
for (var i = 0; i < 5; i++) {
if (i === 2) continue;
console.log(i); // 输出 0, 1, 3, 4(不是 0, 1)
}
// ❌ 误区2:break 只是跳过一次
// ✅ 正确:break 是终止整个循环
for (var i = 0; i < 5; i++) {
if (i === 2) break;
console.log(i); // 输出 0, 1(i=2 时退出循环)
}
实际应用场景:
| 语句 | 典型用途 | 示例场景 |
|---|---|---|
| continue | 过滤特定元素 | 跳过偶数,只处理奇数 |
| continue | 跳过无效数据 | 跳过空值、undefined |
| break | 查找第一个匹配 | 找到目标立即停止 |
| break | 满足条件退出 | 密码正确,停止重试 |
| break | 配合 while(true) | 不确定次数的循环 |
在嵌套循环中的行为:
js
// ⚠️ 重要:break/continue 默认只作用于最近的一层循环
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (j === 1) break; // 只退出内层循环,外层继续
console.log(i, j);
}
}
// 输出:0,0 1,0 2,0(每次内层 j=1 时退出内层,外层 i 继续)
11.4 break 的高级用法:配合 while(true) 取随机数
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>break + while(true) 取随机数</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #263238; color: #ECEFF1; }
button { background: #00BCD4; color: white; border: none; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-size: 1em; }
button:hover { background: #0097A7; }
.result { font-size: 1.5em; margin: 15px 0; color: #80CBC4; }
.code { background: #1e1e1e; padding: 20px; border-radius: 8px; margin-top: 20px; font-family: monospace; line-height: 1.8; }
.k { color: #569CD6; }
.c { color: #6A9955; }
.n { color: #B5CEA8; }
</style>
</head>
<body>
<h2>break + while(true):取大于 0.99 的随机数</h2>
<button onclick="run()">运行一次</button>
<div class="result" id="result">点击按钮查看结果</div>
<div class="code">
<span class="c">// 使用 while(true) + break,等价于之前的 while(rand <= 0.99)</span><br>
<span class="k">while</span> (<span class="k">true</span>) {<br>
<span class="k">var</span> rand = Math.random();<br>
<span class="k">if</span> (rand > <span class="n">0.99</span>) {<br>
<span class="k">break</span>; <span class="c">// 满足条件,跳出死循环</span><br>
}<br>
}<br>
console.log(rand);
</div>
<script>
function run() {
var count = 0;
// 【while(true)】故意创建"死循环"
while (true) { // 条件永远为 true
var rand = Math.random(); // 生成随机数
count++; // 计数
// 【内部退出条件】通过 break 主动跳出
if (rand > 0.99) { // 满足目标条件
break; // 跳出整个 while 循环
}
// 如果不满足,循环继续(回到 while(true))
}
document.getElementById('result').textContent =
'找到:' + rand.toFixed(8) + '(尝试了 ' + count + ' 次)';
}
</script>
</body>
</html>

📝 while(true) + break 模式解析
代码等价性:
js
// 传统写法
var rand = Math.random();
while (rand <= 0.99) {
rand = Math.random();
}
// while(true) + break 写法
while (true) {
var rand = Math.random();
if (rand > 0.99) {
break; // 手动退出
}
}
优劣对比:
| 维度 | 传统 while | while(true) + break |
|---|---|---|
| 可读性 | ✅ 条件直观 | ⚠️ 需要看到 break 才明白 |
| 灵活性 | ⚠️ 复杂条件难写 | ✅ 多条件判断更灵活 |
| 变量初始化 | ❌ 需要循环外初始化 | ✅ 变量可在循环内声明 |
| 退出位置 | 固定(顶部判断) | ✅ 可在循环任意位置 break |
| 适用场景 | 简单条件循环 | 复杂逻辑、多退出点 |
适用场景:
- 多退出条件
js
while (true) {
var input = getUserInput();
if (input === 'quit') break; // 退出条件1
if (input === 'exit') break; // 退出条件2
if (isTimeout()) break; // 退出条件3
processInput(input);
}
- 循环中间退出
js
while (true) {
prepareData(); // 前置操作
var result = processData(); // 处理数据
if (result.success) break; // 中间退出
cleanup(); // 后置操作
}
- 无限服务循环(服务器)
js
while (true) {
var request = waitForRequest(); // 等待请求
handleRequest(request); // 处理请求
// 服务器永远不退出,除非主动停止
}
⚠️ 危险警告
js
// ❌ 真·死循环(没有 break)
while (true) {
console.log('永远不会停止!');
// 浏览器会卡死!必须强制关闭标签页
}
// ✅ 安全的 while(true)
while (true) {
// ... 业务逻辑 ...
if (某个条件) {
break; // 保证有退出路径
}
}
使用建议:
- ✅ 必须确保 100% 有 break 可达
- ✅ 添加注释说明退出条件
- ✅ 考虑添加安全计数器防止意外死循环
- ❌ 不要在生产代码中滥用(可读性差)
安全防护模式:
js
var maxIterations = 1000; // 安全上限
var iterations = 0;
while (true) {
if (iterations++ > maxIterations) {
console.error('循环超时,强制退出');
break; // 安全退出
}
// ... 正常逻辑 ...
if (条件满足) break;
}
11.5 break 与 continue 总结
| 语句 | 作用域 | 效果 | 类比 |
|---|---|---|---|
break |
switch / 循环 | 立即退出当前 switch 或循环 | 跑步途中直接回家 |
continue |
循环 | 跳过本次循环剩余语句,进入下一次 | 跑步途中这一圈走捷径 |
嵌套循环中的 break/continue: 默认只作用于最近的一层循环,不会跳出外层循环。
12 省略大括号的写法
当循环体或分支体只有一条语句 时,可以省略 {},但强烈不推荐在实际项目中使用。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>省略大括号演示</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.compare { display: flex; gap: 20px; flex-wrap: wrap; }
.box { flex: 1; min-width: 300px; background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
pre { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 6px; overflow-x: auto; }
.warn { background: #FFF8E1; border: 1px solid #FFC107; border-radius: 6px; padding: 12px; margin-top: 15px; }
.good { background: #E8F5E9; border: 1px solid #4CAF50; border-radius: 6px; padding: 12px; margin-top: 15px; }
</style>
</head>
<body>
<h2>省略大括号:写法对比</h2>
<div class="compare">
<div class="box">
<h3>❌ 省略大括号(不推荐)</h3>
<pre>for (var i = 0; i < 10; i++)
console.log('hello', i);</pre>
<div class="warn">
⚠️ 危险:只有紧跟的第一条语句属于循环体。<br>
如果后来添加第二条语句忘记加括号,会出现逻辑 Bug!
</div>
</div>
<div class="box">
<h3>✅ 保留大括号(推荐)</h3>
<pre>for (var i = 0; i < 10; i++) {
console.log('hello', i);
}</pre>
<div class="good">
✅ 即使只有一条语句,保留括号可以:<br>
1. 提高可读性<br>
2. 方便后续添加语句<br>
3. 避免隐性 Bug
</div>
</div>
</div>
<script>
// 省略大括号示例
for (var i = 0; i < 3; i++)
console.log('省略括号 hello', i);
console.log('循环后仍会执行的代码', i); // 循环外的代码
// 陷阱演示
if (false)
console.log('这行不会执行');
// 下面这行永远执行,不受 if 控制!
console.log('这行始终执行,不在 if 的管控范围内');
</script>
</body>
</html>

📝 代码解释:省略大括号的陷阱
语法规则:单条语句可省略括号
js
// ✅ 标准写法(推荐)
for (var i = 0; i < 10; i++) {
console.log('hello', i); // 大括号明确了循环体范围
}
// ⚠️ 省略大括号(不推荐)
for (var i = 0; i < 10; i++)
console.log('hello', i); // 只有这一行属于循环体
陷阱演示:添加第二条语句
js
// ❌ 常见错误:以为两行都在循环内
for (var i = 0; i < 3; i++)
console.log('省略括号 hello', i); // ← 循环体(会执行3次)
console.log('循环后仍会执行的代码', i); // ← 不在循环体内(只执行1次)
// 实际输出:
// 省略括号 hello 0
// 省略括号 hello 1
// 省略括号 hello 2
// 循环后仍会执行的代码 3 ← 循环结束后才执行
if 语句的陷阱:
js
// ❌ 危险写法
if (false)
console.log('这行不会执行'); // ← if 控制的语句
console.log('这行始终执行,不在 if 的管控范围内'); // ← if 外的语句,总是执行
// ✅ 正确写法(添加大括号)
if (false) {
console.log('这行不会执行');
console.log('这行也不会执行'); // 现在两行都在 if 内
}
缩进不等于作用域!
js
// ❌ 缩进并不能改变作用域
for (var i = 0; i < 3; i++)
console.log('第一行', i); // ← 循环体
console.log('第二行', i); // ← 不在循环体内(缩进只是视觉效果)
// 相当于:
for (var i = 0; i < 3; i++)
console.log('第一行', i);
console.log('第二行', i); // 循环外
为何强烈不推荐:
| 风险 | 说明 | 示例 |
|---|---|---|
| 维护困难 | 后续添加语句容易忘记加括号 | 加第二行代码时没注意 |
| 逻辑错误 | 缩进误导,实际作用域不同 | 以为两行都在循环内 |
| 团队协作 | 代码审查时容易遗漏 | 别人修改时出错 |
| 调试困难 | Bug 隐蔽,不易发现 | 逻辑看起来对,实际错了 |
最佳实践:
✅ 永远使用大括号,即使只有一条语句
✅ 提高代码可读性和可维护性
✅ 避免隐性 Bug
✅ 团队协作更安全
代码规范建议:
js
// ✅ 推荐:始终使用大括号
if (condition) {
doSomething();
}
for (var i = 0; i < n; i++) {
process(i);
}
while (condition) {
execute();
}
// ❌ 不推荐:省略大括号
if (condition) doSomething();
for (var i = 0; i < n; i++) process(i);
while (condition) execute();
13 分支作业案例讲解
13.1 案例一:字符串拼接(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>字符串拼接 - 自我介绍生成器</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; background: #f5f5f5; }
.form { background: white; border-radius: 12px; padding: 30px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); }
.form h2 { margin-top: 0; color: #333; text-align: center; }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 5px; color: #555; font-weight: bold; }
.form-group input { width: 100%; padding: 10px; border: 2px solid #ddd; border-radius: 8px; box-sizing: border-box; font-size: 1em; }
.form-group input:focus { outline: none; border-color: #2196F3; }
button { width: 100%; background: #2196F3; color: white; border: none; padding: 12px; border-radius: 8px; cursor: pointer; font-size: 1.1em; margin-top: 10px; }
button:hover { background: #1976D2; }
.result { margin-top: 20px; background: #E3F2FD; border: 2px solid #2196F3; border-radius: 8px; padding: 20px; display: none; }
.result p { margin: 0; font-size: 1.1em; line-height: 1.8; color: #1565C0; }
</style>
</head>
<body>
<div class="form">
<h2>自我介绍生成器</h2>
<div class="form-group">
<label>姓名</label>
<input type="text" id="name" placeholder="请输入您的姓名">
</div>
<div class="form-group">
<label>性别</label>
<input type="text" id="gender" placeholder="男 / 女">
</div>
<div class="form-group">
<label>家乡</label>
<input type="text" id="hometown" placeholder="例如:上海">
</div>
<div class="form-group">
<label>业余爱好</label>
<input type="text" id="hobby" placeholder="例如:编程、阅读">
</div>
<div class="form-group">
<label>期望薪资(K)</label>
<input type="number" id="salary" placeholder="例如:15">
</div>
<button onclick="generate()">生成自我介绍</button>
<div class="result" id="result">
<p id="intro"></p>
</div>
</div>
<script>
function generate() {
var name = document.getElementById('name').value || '匿名用户';
var gender = document.getElementById('gender').value || '未知';
var hometown = document.getElementById('hometown').value || '未知';
var hobby = document.getElementById('hobby').value || '未填写';
var salary = document.getElementById('salary').value || '0';
// 字符串拼接(+运算符)
var message = '我的名字叫' + name + ',性别' + gender +
',我来自' + hometown + ',我的业余爱好是' + hobby +
',我学习后的期望薪资是' + salary + ' K。';
document.getElementById('intro').textContent = message;
document.getElementById('result').style.display = 'block';
}
</script>
</body>
</html>

📝 代码解释:字符串拼接与默认值
核心技术:|| 运算符提供默认值
js
var name = document.getElementById('name').value || '匿名用户';
// ↑ 获取输入框的值 ↑ 如果为空字符串,使用默认值
|| 运算符的短路特性:
js
// 逻辑:如果左边为假值(falsy),返回右边的值
值 || 默认值
// 假值(falsy)包括:
// '', 0, false, null, undefined, NaN
示例:
'' || '默认值' → '默认值'(空字符串是假值)
'实际值' || '默认值' → '实际值'(非空字符串是真值)
0 || 10 → 10(0 是假值)
获取输入并设置默认值:
js
// 获取各个输入框的值,如果为空则使用默认值
var name = document.getElementById('name').value || '匿名用户'; // 空→'匿名用户'
var gender = document.getElementById('gender').value || '未知'; // 空→'未知'
var hometown = document.getElementById('hometown').value || '未知'; // 空→'未知'
var hobby = document.getElementById('hobby').value || '未填写'; // 空→'未填写'
var salary = document.getElementById('salary').value || '0'; // 空→'0'
字符串拼接技巧:
js
// 方式1:简单拼接(使用 + 运算符)
var message = '我的名字叫' + name + ',性别' + gender + ',我来自' + hometown;
// 方式2:多行拼接(提高可读性)
var message = '我的名字叫' + name + ',性别' + gender +
',我来自' + hometown + ',我的业余爱好是' + hobby +
',我学习后的期望薪资是' + salary + ' K。';
// 方式3:ES6 模板字符串(现代推荐)
var message = `我的名字叫${name},性别${gender},我来自${hometown},
我的业余爱好是${hobby},我学习后的期望薪资是${salary} K。`;
DOM 操作:
js
// 设置文本内容
document.getElementById('intro').textContent = message;
// textContent:纯文本,安全(防止 XSS 攻击)
// innerHTML:HTML 内容,有安全风险
// 显示结果区域
document.getElementById('result').style.display = 'block';
// 从 display: none → display: block
完整执行流程:
用户点击按钮
↓
触发 generate() 函数
↓
① 获取所有输入框的值(空值用默认值替代)
↓
② 使用 + 运算符拼接成完整字符串
↓
③ 将字符串设置到 #intro 元素的 textContent
↓
④ 显示结果区域(display: block)
↓
用户看到生成的自我介绍
知识点总结:
| 技术点 | 说明 | 应用场景 |
|---|---|---|
| ** | 运算符** | |
| 字符串拼接 | + 运算符连接字符串 | 动态生成文本 |
| .value | 获取输入框的值 | 表单数据获取 |
| .textContent | 设置纯文本内容 | 安全地显示用户输入 |
| .style.display | 控制元素显示/隐藏 | 动态UI交互 |
实际应用场景:
- ✅ 表单数据收集与展示
- ✅ 用户信息卡片生成
- ✅ 简历自动生成
- ✅ 评论/留言预览
13.2 案例二:阶梯水价(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阶梯水价计算器</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; max-width: 500px; margin: 40px auto; padding: 20px; background: #E3F2FD; }
.calculator { background: white; border-radius: 16px; padding: 30px; box-shadow: 0 8px 32px rgba(33,150,243,0.2); }
h2 { color: #1565C0; text-align: center; margin-top: 0; }
.price-table { background: #F5F5F5; border-radius: 8px; padding: 15px; margin: 20px 0; }
.price-table table { width: 100%; border-collapse: collapse; }
.price-table th, .price-table td { padding: 8px 12px; text-align: left; border-bottom: 1px solid #ddd; }
.price-table th { color: #555; }
input { width: 100%; padding: 12px; border: 2px solid #90CAF9; border-radius: 8px; box-sizing: border-box; font-size: 1.1em; text-align: center; }
input:focus { outline: none; border-color: #2196F3; }
button { width: 100%; background: linear-gradient(135deg, #2196F3, #21CBF3); color: white; border: none; padding: 14px; border-radius: 8px; cursor: pointer; font-size: 1.1em; margin-top: 15px; font-weight: bold; }
button:hover { opacity: 0.9; }
.result { margin-top: 20px; text-align: center; display: none; }
.amount { font-size: 2.5em; color: #1565C0; font-weight: bold; }
.detail { color: #777; font-size: 0.9em; margin-top: 8px; }
.error { color: #f44336; text-align: center; margin-top: 15px; display: none; }
</style>
</head>
<body>
<div class="calculator">
<h2>💧 阶梯水价计算器</h2>
<div class="price-table">
<table>
<tr><th>用水量</th><th>单价</th></tr>
<tr><td>0 ~ 220 m³</td><td>4.05 元/m³</td></tr>
<tr><td>220 ~ 300 m³</td><td>5.80 元/m³</td></tr>
<tr><td>300 m³ 以上</td><td>8.79 元/m³</td></tr>
</table>
</div>
<input type="number" id="waterInput" placeholder="请输入用水量(m³)" min="0">
<button onclick="calculate()">计算水费</button>
<div class="error" id="error">请输入有效的用水量(正数)</div>
<div class="result" id="result">
<div class="amount">¥<span id="totalAmount"></span></div>
<div class="detail" id="detail"></div>
</div>
</div>
<script>
function calculate() {
var val = parseFloat(document.getElementById('waterInput').value);
document.getElementById('error').style.display = 'none';
document.getElementById('result').style.display = 'none';
if (isNaN(val) || val < 0) {
document.getElementById('error').style.display = 'block';
return;
}
var total, detail;
if (val <= 220) {
total = 4.05 * val;
detail = val + ' m³ × 4.05 = ' + total.toFixed(2) + ' 元';
} else if (val <= 300) {
var part1 = 220 * 4.05;
var part2 = (val - 220) * 5.8;
total = part1 + part2;
detail = '220m³×4.05 + ' + (val-220).toFixed(0) + 'm³×5.80 = ' + total.toFixed(2) + ' 元';
} else {
var part1 = 220 * 4.05;
var part2 = 80 * 5.8;
var part3 = (val - 300) * 8.79;
total = part1 + part2 + part3;
detail = '220m³×4.05 + 80m³×5.80 + ' + (val-300).toFixed(0) + 'm³×8.79 = ' + total.toFixed(2) + ' 元';
}
document.getElementById('totalAmount').textContent = total.toFixed(2);
document.getElementById('detail').textContent = detail;
document.getElementById('result').style.display = 'block';
}
</script>
</body>
</html>

📝 代码解释:阶梯计价算法
核心知识:if-else if-else 多分支判断
js
// 阶梯定价规则:
// 0~220m³:4.05 元/m³
// 220~300m³:5.80 元/m³(超出220的部分)
// 300m³以上:8.79 元/m³(超出300的部分)
① 输入验证:
js
var val = parseFloat(document.getElementById('waterInput').value);
// ↑ parseFloat:将字符串转换为浮点数
// 清空之前的提示
document.getElementById('error').style.display = 'none';
document.getElementById('result').style.display = 'none';
// 验证输入是否合法
if (isNaN(val) || val < 0) {
// ↑ isNaN:检查是否是非法数字
// ↑ 检查是否为负数
document.getElementById('error').style.display = 'block';
return; // 提前退出函数
}
② 第一档(0~220m³):简单计算
js
if (val <= 220) {
total = 4.05 * val; // 直接乘以单价
detail = val + ' m³ × 4.05 = ' + total.toFixed(2) + ' 元';
// toFixed(2):保留2位小数
}
- 示例: 用水 100m³
- total = 4.05 × 100 = 405 元
- detail = "100 m³ × 4.05 = 405.00 元"
③ 第二档(220~300m³):分段计算
js
else if (val <= 300) {
var part1 = 220 * 4.05; // 前220m³按第一档价格
var part2 = (val - 220) * 5.8; // 超出220的部分按第二档价格
total = part1 + part2; // 总价 = 第一段 + 第二段
detail = '220m³×4.05 + ' + (val-220).toFixed(0) + 'm³×5.80 = ' + total.toFixed(2) + ' 元';
}
- 示例: 用水 250m³
- part1 = 220 × 4.05 = 891 元(前220m³)
- part2 = (250 - 220) × 5.8 = 30 × 5.8 = 174 元(超出部分)
- total = 891 + 174 = 1065 元
- detail = "220m³×4.05 + 30m³×5.80 = 1065.00 元"
④ 第三档(300m³以上):三段计算
js
else {
var part1 = 220 * 4.05; // 前220m³按第一档
var part2 = 80 * 5.8; // 220~300m³(共80m³)按第二档
var part3 = (val - 300) * 8.79; // 超出300的部分按第三档
total = part1 + part2 + part3; // 总价 = 三段之和
detail = '220m³×4.05 + 80m³×5.80 + ' + (val-300).toFixed(0) + 'm³×8.79 = ' + total.toFixed(2) + ' 元';
}
- 示例: 用水 350m³
- part1 = 220 × 4.05 = 891 元(第一档)
- part2 = 80 × 5.8 = 464 元(第二档)
- part3 = (350 - 300) × 8.79 = 50 × 8.79 = 439.5 元(第三档)
- total = 891 + 464 + 439.5 = 1794.5 元
- detail = "220m³×4.05 + 80m³×5.80 + 50m³×8.79 = 1794.50 元"
⑤ 显示结果:
js
document.getElementById('totalAmount').textContent = total.toFixed(2); // 显示总金额
document.getElementById('detail').textContent = detail; // 显示计算明细
document.getElementById('result').style.display = 'block'; // 显示结果区域
关键函数说明:
| 函数 | 作用 | 示例 |
|---|---|---|
| parseFloat() | 字符串转浮点数 | parseFloat('3.14') → 3.14 |
| isNaN() | 检查是否为非法数字 | isNaN('abc') → true |
| .toFixed(n) | 保留n位小数 | (3.1415).toFixed(2) → '3.14' |
算法特点:
- ✅ 分段累加:每一档的费用分开计算,最后求和
- ✅ 边界清晰:<=220、<=300、else 三个分支互斥
- ✅ 易于扩展:新增阶梯只需添加 else if 分支
实际应用场景:
- 💧 水电燃气阶梯计价
- 📱 手机流量套餐计费
- 🚕 出租车分段计价
- 💰 累进税率计算
可视化执行流程:
输入用水量
↓
parseFloat 转换为数字
↓
验证是否合法(isNaN、<0)
↓
├─ 合法 → 判断所属区间
│ ├─ val<=220 → 单价计算
│ ├─ 220<val<=300 → 两段计算
│ └─ val>300 → 三段计算
│ ↓
│ 显示结果
│
└─ 不合法 → 显示错误提示
13.3 案例三:体重判断(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体型判断工具</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; max-width: 480px; margin: 40px auto; padding: 20px; background: #FFF3E0; }
.card { background: white; border-radius: 16px; padding: 30px; box-shadow: 0 8px 24px rgba(255,152,0,0.2); }
h2 { color: #E65100; text-align: center; margin-top: 0; }
.input-group { margin: 20px 0; }
input[type=range] { width: 100%; height: 6px; appearance: none; background: linear-gradient(to right, #FF9800, #FF5722); border-radius: 3px; outline: none; }
.weight-display { text-align: center; font-size: 3em; font-weight: bold; color: #E65100; margin: 10px 0; }
button { width: 100%; background: linear-gradient(135deg, #FF9800, #FF5722); color: white; border: none; padding: 14px; border-radius: 8px; cursor: pointer; font-size: 1.1em; font-weight: bold; }
.result { text-align: center; margin-top: 20px; display: none; }
.category { font-size: 2.5em; font-weight: bold; margin: 10px 0; }
.range-hint { color: #999; font-size: 0.85em; }
.scale { display: flex; justify-content: space-between; margin-top: 5px; font-size: 0.75em; color: #bbb; }
</style>
</head>
<body>
<div class="card">
<h2>体型判断工具</h2>
<div class="input-group">
<div class="weight-display"><span id="weightVal">80</span> kg</div>
<input type="range" id="weightSlider" min="30" max="250" value="80"
oninput="document.getElementById('weightVal').textContent=this.value">
<div class="scale"><span>30kg</span><span>140kg</span><span>250kg</span></div>
</div>
<button onclick="judge()">判断体型</button>
<div class="result" id="result">
<div class="category" id="category"></div>
<div class="range-hint" id="hint"></div>
</div>
</div>
<script>
var categories = [
{ max: 60, label: '偏瘦 🥢', hint: '体重低于 60kg', color: '#2196F3' },
{ max: 80, label: '骨感 💃', hint: '60 ~ 80kg', color: '#4CAF50' },
{ max: 100, label: '苗条 ✨', hint: '80 ~ 100kg', color: '#8BC34A' },
{ max: 120, label: '健康 💪', hint: '100 ~ 120kg', color: '#FF9800' },
{ max: 140, label: '丰满 🌸', hint: '120 ~ 140kg', color: '#FF5722' },
{ max: 180, label: '微胖 🌟', hint: '140 ~ 180kg', color: '#E91E63' },
{ max: Infinity, label: '偏胖 🏋️', hint: '180kg 以上', color: '#9C27B0' }
];
function judge() {
var weight = parseInt(document.getElementById('weightSlider').value);
var result = categories.find(function(c) { return weight < c.max; });
document.getElementById('category').textContent = result.label;
document.getElementById('category').style.color = result.color;
document.getElementById('hint').textContent = '体重范围:' + result.hint;
document.getElementById('result').style.display = 'block';
}
</script>
</body>
</html>

📝 代码解释:数组查找与动态样式
核心知识:数组 + find 方法
① 数据结构:配置数组
js
var categories = [
{ max: 60, label: '偏瘦 🥢', hint: '体重低于 60kg', color: '#2196F3' },
{ max: 80, label: '骨感 💃', hint: '60 ~ 80kg', color: '#4CAF50' },
{ max: 100, label: '苗条 ✨', hint: '80 ~ 100kg', color: '#8BC34A' },
// ... 更多分类
{ max: Infinity, label: '偏胖 🏋️', hint: '180kg 以上', color: '#9C27B0' }
];
// 数组中每个对象包含:最大值、标签、提示、颜色
// Infinity:JavaScript 表示无穷大的特殊值
② 获取输入值:
js
var weight = parseInt(document.getElementById('weightSlider').value);
// ↑ parseInt:将字符串转换为整数(舍去小数部分)
// ↑ range 输入框的值
③ 查找匹配的分类:
js
var result = categories.find(function(c) {
return weight < c.max;
});
// find 方法:遍历数组,返回第一个满足条件的元素
find 方法执行流程:
假设 weight = 75
第1次迭代:c = {max: 60, ...}
判断:75 < 60? → false → 继续
第2次迭代:c = {max: 80, ...}
判断:75 < 80? → true → 返回这个对象
result = { max: 80, label: '骨感 💃', hint: '60 ~ 80kg', color: '#4CAF50' }
停止查找
为什么最后一个用 Infinity?
js
// 如果所有 max 都不满足,最终会匹配 Infinity
weight < Infinity // 永远为 true(任何数字都小于无穷大)
// 这样可以保证一定能找到匹配项,避免 find 返回 undefined
④ 显示结果并应用样式:
js
// 设置分类文本
document.getElementById('category').textContent = result.label; // '骨感 💃'
// 设置动态颜色
document.getElementById('category').style.color = result.color; // '#4CAF50'
// 设置提示文本
document.getElementById('hint').textContent = '体重范围:' + result.hint; // '体重范围:60 ~ 80kg'
// 显示结果区域
document.getElementById('result').style.display = 'block';
⑤ 实时交互:oninput 事件
html
<input type="range" id="weightSlider" min="30" max="250" value="80"
oninput="document.getElementById('weightVal').textContent=this.value">
<!-- ↑ 滑动时实时更新显示的数字 ↑ 当前滑块的值 -->
关键API说明:
| API | 作用 | 示例 |
|---|---|---|
| parseInt() | 字符串转整数 | parseInt('75.9') → 75 |
| Array.find() | 查找数组中第一个满足条件的元素 | arr.find(item => item > 10) |
| .style.color | 动态设置文本颜色 | element.style.color = '#FF0000' |
| oninput | 输入框值变化时触发事件 | οninput="updateValue()" |
| Infinity | 表示无穷大 | 100 < Infinity → true |
算法优势:
- ✅ 数据驱动:分类规则存在数组中,易于维护
- ✅ 自动匹配:find 方法自动查找,无需写多个 if-else
- ✅ 易于扩展:新增分类只需在数组中添加对象
- ✅ 动态样式:通过 .style 属性动态设置颜色
传统写法 vs 现代写法对比:
js
// ❌ 传统写法:多个 if-else
if (weight < 60) {
label = '偏瘦 🥢';
color = '#2196F3';
} else if (weight < 80) {
label = '骨感 💃';
color = '#4CAF50';
} else if (weight < 100) {
// ...
}
// ✅ 现代写法:数据驱动 + find
var categories = [/* 配置数组 */];
var result = categories.find(c => weight < c.max);
实际应用场景:
- 📊 数据分级展示(成绩等级、信用评分)
- 🎯 动态样式切换(根据数据变化颜色)
- 📈 阈值判断(温度预警、库存预警)
- 🏆 等级系统(用户等级、VIP等级)
13.4 案例四:闰年判断(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闰年判断器</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; max-width: 500px; margin: 40px auto; padding: 20px; background: #E8EAF6; }
.card { background: white; border-radius: 16px; padding: 30px; box-shadow: 0 8px 24px rgba(63,81,181,0.2); }
h2 { color: #3F51B5; text-align: center; margin-top: 0; }
.rule { background: #F5F5F5; border-radius: 8px; padding: 15px; margin: 15px 0; font-size: 0.9em; line-height: 1.8; }
.rule h4 { margin: 0 0 8px; color: #555; }
input { width: 100%; padding: 12px; border: 2px solid #C5CAE9; border-radius: 8px; box-sizing: border-box; font-size: 1.2em; text-align: center; }
input:focus { outline: none; border-color: #3F51B5; }
button { width: 100%; background: linear-gradient(135deg, #3F51B5, #673AB7); color: white; border: none; padding: 14px; border-radius: 8px; cursor: pointer; font-size: 1.1em; font-weight: bold; margin-top: 15px; }
.result { text-align: center; margin-top: 20px; padding: 20px; border-radius: 8px; display: none; }
.leap { background: #E8F5E9; color: #2E7D32; }
.non-leap { background: #FFEBEE; color: #C62828; }
.result .year { font-size: 2em; font-weight: bold; }
.result .verdict { font-size: 1.5em; margin-top: 5px; }
.logic { font-size: 0.85em; margin-top: 8px; color: #777; }
</style>
</head>
<body>
<div class="card">
<h2>📅 闰年判断器</h2>
<div class="rule">
<h4>闰年规则:</h4>
① 非世纪年(不能被100整除):能被 4 整除 → 闰年<br>
② 世纪年(能被100整除):必须能被 400 整除 → 闰年<br>
<strong>综合条件:(year%4===0 && year%100!==0) || year%400===0</strong>
</div>
<input type="number" id="yearInput" placeholder="请输入年份" value="2024">
<button onclick="check()">判断是否是闰年</button>
<div class="result" id="result">
<div class="year" id="yearDisplay"></div>
<div class="verdict" id="verdict"></div>
<div class="logic" id="logic"></div>
</div>
</div>
<script>
function check() {
var year = parseInt(document.getElementById('yearInput').value);
if (isNaN(year)) {
alert('请输入有效的年份!');
return;
}
var isLeap = (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
var resultEl = document.getElementById('result');
document.getElementById('yearDisplay').textContent = year + ' 年';
document.getElementById('verdict').textContent = isLeap ? '✅ 是闰年!' : '❌ 不是闰年';
var logicText = '';
if (year % 400 === 0) {
logicText = year + ' 能被 400 整除(世纪年闰年)';
} else if (year % 100 === 0) {
logicText = year + ' 能被 100 整除但不能被 400 整除(世纪年平年)';
} else if (year % 4 === 0) {
logicText = year + ' 能被 4 整除且不是世纪年(非世纪年闰年)';
} else {
logicText = year + ' 不能被 4 整除(平年)';
}
document.getElementById('logic').textContent = logicText;
resultEl.className = 'result ' + (isLeap ? 'leap' : 'non-leap');
resultEl.style.display = 'block';
}
</script>
</body>
</html>

📝 代码解释:闰年判断的复合逻辑
核心知识:逻辑运算符的复合使用
① 闰年规则(天文学定义):
规则1:能被 4 整除 → 闰年(基本规则)
规则2:能被 100 整除 → 平年(世纪年例外)
规则3:能被 400 整除 → 闰年(世纪年闰年)
综合逻辑:
(能被4整除 且 不能被100整除) 或 (能被400整除)
② JavaScript 表达式:
js
var isLeap = (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
// ↑ 条件1:非世纪年闰年 ↑ 或 ↑ 条件2:世纪年闰年
// (year % 4 === 0 && year % 100 !== 0):能被4整除但不能被100整除
// || (year % 400 === 0):或者能被400整除
③ 逻辑拆解(优先级):
js
// 优先级:&& 高于 ||
// 相当于:
var isLeap = (条件A && 条件B) || 条件C;
// 展开:
var 条件A = (year % 4 === 0); // 能被 4 整除
var 条件B = (year % 100 !== 0); // 不能被 100 整除
var 条件C = (year % 400 === 0); // 能被 400 整除
var isLeap = (条件A && 条件B) || 条件C;
④ 典型年份测试:
js
// 示例1:2024(非世纪年,能被4整除)
2024 % 4 === 0 // true
2024 % 100 !== 0 // true(2024 % 100 = 24,不为0)
(true && true) || false → true → 闰年 ✅
// 示例2:1900(世纪年,能被100整除但不能被400整除)
1900 % 4 === 0 // true
1900 % 100 !== 0 // false(1900 % 100 = 0)
1900 % 400 === 0 // false(1900 % 400 = 300)
(true && false) || false → false → 平年 ❌
// 示例3:2000(世纪年,能被400整除)
2000 % 4 === 0 // true
2000 % 100 !== 0 // false
2000 % 400 === 0 // true
(true && false) || true → true → 闰年 ✅
// 示例4:2023(不能被4整除)
2023 % 4 === 0 // false(2023 % 4 = 3)
2023 % 100 !== 0 // true
2023 % 400 === 0 // false
(false && true) || false → false → 平年 ❌
⑤ 详细判断逻辑(用于展示):
js
var logicText = '';
if (year % 400 === 0) {
// 优先级最高:能被400整除,一定是闰年
logicText = year + ' 能被 400 整除(世纪年闰年)';
} else if (year % 100 === 0) {
// 次优先级:能被100整除但不能被400整除,一定是平年
logicText = year + ' 能被 100 整除但不能被 400 整除(世纪年平年)';
} else if (year % 4 === 0) {
// 再次:能被4整除且不是世纪年,一定是闰年
logicText = year + ' 能被 4 整除且不是世纪年(非世纪年闰年)';
} else {
// 最后:不能被4整除,一定是平年
logicText = year + ' 不能被 4 整除(平年)';
}
⑥ 三元运算符:简洁的条件赋值
js
var verdict = isLeap ? '✅ 是闰年!' : '❌ 不是闰年';
// ↑ 条件 ↑ true时的值 ↑ false时的值
// 等价于:
var verdict;
if (isLeap) {
verdict = '✅ 是闰年!';
} else {
verdict = '❌ 不是闰年';
}
⑦ 动态 CSS 类:
js
resultEl.className = 'result ' + (isLeap ? 'leap' : 'non-leap');
// 如果是闰年:className = 'result leap'(绿色背景)
// 如果是平年:className = 'result non-leap'(红色背景)
关键运算符总结:
| 运算符 | 作用 | 示例 |
|---|---|---|
| % | 取模(取余数) | 10 % 3 → 1 |
| === | 严格相等 | 5 === 5 → true |
| !== | 严格不等 | 5 !== 3 → true |
| && | 逻辑与(两个都true才true) | true && false → false |
| || | 逻辑或(一个true就true) | true || false → true |
| ? : | 三元运算符(条件表达式) | x>10 ? 'big' : 'small' |
逻辑优先级:
优先级从高到低:
1. % (取模)
2. === 、!== (比较)
3. && (逻辑与)
4. || (逻辑或)
示例:
year % 4 === 0 && year % 100 !== 0 || year % 400 === 0
↓ 先计算 %
(year % 4) === 0 && (year % 100) !== 0 || (year % 400) === 0
↓ 再计算 === 、!==
(result1) && (result2) || (result3)
↓ 再计算 &&
(result4) || (result3)
↓ 最后计算 ||
最终结果
实际应用场景:
- 📅 日历系统(计算2月天数)
- 🎂 生日提醒(计算年龄)
- 📊 历史数据分析(按年份统计)
- 🔄 定期任务调度(每4年执行一次)
14 强化练习题完整解答
练习一:while 循环写四种形式的九九乘法表
(见第 10 章,已完整演示)
练习二:100-200 之间能被 3 或 7 整除的数(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>能被3或7整除的数</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.result { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.num { padding: 8px 14px; border-radius: 20px; font-weight: bold; }
.by3 { background: #E3F2FD; color: #1565C0; }
.by7 { background: #F3E5F5; color: #6A1B9A; }
.by37 { background: linear-gradient(135deg, #E3F2FD, #F3E5F5); color: #333; border: 2px solid #9C27B0; }
.legend { display: flex; gap: 15px; margin-top: 15px; flex-wrap: wrap; }
.legend-item { display: flex; align-items: center; gap: 6px; }
.dot { padding: 3px 10px; border-radius: 12px; font-size: 0.85em; }
.stats { background: white; border-radius: 8px; padding: 15px; margin-top: 15px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
</style>
</head>
<body>
<h2>100-200 之间能被 3 或 7 整除的数</h2>
<div class="legend">
<div class="legend-item"><span class="dot by3">蓝色</span>能被 3 整除</div>
<div class="legend-item"><span class="dot by7">紫色</span>能被 7 整除</div>
<div class="legend-item"><span class="dot by37" style="border:2px solid #9C27B0;">边框</span>同时能被 3 和 7 整除</div>
</div>
<div class="result" id="result"></div>
<div class="stats" id="stats"></div>
<script>
var container = document.getElementById('result');
var count3 = 0, count7 = 0, count37 = 0, total = 0;
var i = 100;
while (i <= 200) {
if (i % 3 === 0 || i % 7 === 0) {
var div = document.createElement('div');
div.textContent = i;
if (i % 3 === 0 && i % 7 === 0) {
div.className = 'num by37';
count37++;
} else if (i % 3 === 0) {
div.className = 'num by3';
count3++;
} else {
div.className = 'num by7';
count7++;
}
container.appendChild(div);
total++;
}
i++;
}
document.getElementById('stats').innerHTML =
'共找到 <strong>' + total + '</strong> 个数字:' +
'仅被3整除 <strong>' + count3 + '</strong> 个,' +
'仅被7整除 <strong>' + count7 + '</strong> 个,' +
'同时被3和7整除 <strong>' + count37 + '</strong> 个';
</script>
</body>
</html>

📝 代码解释:条件过滤与分类统计
核心知识:嵌套条件判断 + 计数器
① 初始化变量:
js
var container = document.getElementById('result'); // DOM 容器
var count3 = 0, count7 = 0, count37 = 0, total = 0; // 计数器
// ↑ 仅被3整除 ↑ 仅被7整除 ↑ 同时整除 ↑ 总数
② 主循环:while 遍历 100-200
js
var i = 100;
while (i <= 200) {
// 外层条件:能被 3 或 7 整除
if (i % 3 === 0 || i % 7 === 0) {
// 满足条件,进行处理
}
i++; // 循环变量更新
}
③ 嵌套分类(三种情况):
js
if (i % 3 === 0 || i % 7 === 0) { // 外层:至少满足一个条件
var div = document.createElement('div'); // 创建 DOM 元素
div.textContent = i; // 设置文本内容
// 内层:精确分类
if (i % 3 === 0 && i % 7 === 0) { // 情况1:同时能被 3 和 7 整除
div.className = 'num by37'; // 特殊样式(渐变+边框)
count37++; // 计数器 +1
} else if (i % 3 === 0) { // 情况2:仅能被 3 整除
div.className = 'num by3'; // 蓝色样式
count3++;
} else { // 情况3:仅能被 7 整除
div.className = 'num by7'; // 紫色样式
count7++;
}
container.appendChild(div); // 添加到页面
total++; // 总计数 +1
}
④ 分类逻辑流程图:
检查数字 i
↓
i%3===0 或 i%7===0?
↓ 是
创建 DOM 元素
↓
i%3===0 且 i%7===0?
↓ 是 ↓ 否
【同时整除】 i%3===0?
count37++ ↓ 是 ↓ 否
样式:by37 【仅被3整除】【仅被7整除】
count3++ count7++
样式:by3 样式:by7
↓
添加到页面,total++
⑤ 典型数字分类:
js
// 示例1:105(既能被3整除,也能被7整除)
105 % 3 === 0 // true (105 ÷ 3 = 35)
105 % 7 === 0 // true (105 ÷ 7 = 15)
// 结果:count37++,样式 by37
// 示例2:102(仅能被3整除)
102 % 3 === 0 // true (102 ÷ 3 = 34)
102 % 7 === 0 // false (102 ÷ 7 = 14...4)
// 结果:count3++,样式 by3
// 示例3:112(仅能被7整除)
112 % 3 === 0 // false (112 ÷ 3 = 37...1)
112 % 7 === 0 // true (112 ÷ 7 = 16)
// 结果:count7++,样式 by7
// 示例4:101(都不能整除)
101 % 3 === 0 // false
101 % 7 === 0 // false
// 结果:不进入 if,跳过
⑥ 动态 DOM 创建:
js
var div = document.createElement('div'); // 创建 <div> 元素
div.textContent = i; // 设置文本(等价于 <div>105</div>)
div.className = 'num by37'; // 设置 CSS 类
container.appendChild(div); // 添加到容器中
⑦ 统计结果输出:
js
document.getElementById('stats').innerHTML =
'共找到 <strong>' + total + '</strong> 个数字:' +
'仅被3整除 <strong>' + count3 + '</strong> 个,' +
'仅被7整除 <strong>' + count7 + '</strong> 个,' +
'同时被3和7整除 <strong>' + count37 + '</strong> 个';
// innerHTML:允许插入 HTML 标签(<strong> 加粗)
⑧ 数学验证(100-200 范围):
能被 3 整除的数:102, 105, 108, ..., 198(33个)
能被 7 整除的数:105, 112, 119, ..., 196(15个)
同时能被 3 和 7 整除的数:105, 126, 147, 168, 189(5个)
分类计数:
count3 = 33 - 5 = 28(仅被3整除)
count7 = 15 - 5 = 10(仅被7整除)
count37 = 5(同时整除)
total = 28 + 10 + 5 = 43(总数)
验证:28 + 10 + 5 = 43 ✓
知识点总结:
| 知识点 | 作用 | 应用场景 |
|---|---|---|
| ** | 逻辑或** | |
| && 逻辑与 | 必须同时满足 | 精确匹配 |
| % 取模运算 | 判断整除性 | 倍数检测 |
| 嵌套 if | 多级分类 | 精细化筛选 |
| createElement | 动态创建 DOM | 动态生成内容 |
| appendChild | 添加子节点 | DOM 操作 |
| 计数器模式 | 统计数量 | 数据聚合 |
实际应用场景:
- 🔢 数据筛选(满足特定条件的记录)
- 📊 分类统计(按多个维度分类)
- 🎨 动态样式(根据数据特征应用不同样式)
- 📈 可视化展示(数据驱动的UI渲染)
练习三:三位数中各位上有3或7的数(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>各位上有3或7的三位数</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.result { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 20px; }
.num { padding: 6px 12px; background: white; border-radius: 6px; font-size: 0.9em; box-shadow: 0 1px 4px rgba(0,0,0,0.1); position: relative; overflow: hidden; }
.digit-3 { color: #f44336; font-weight: bold; }
.digit-7 { color: #2196F3; font-weight: bold; }
.stats { background: white; border-radius: 8px; padding: 15px; margin-top: 15px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
h2 { border-left: 4px solid #673AB7; padding-left: 10px; }
</style>
</head>
<body>
<h2>100-999 中各位上有 3 或 7 的数字</h2>
<p style="color:#555">(<span class="digit-3">红色</span>表示含3,<span class="digit-7">蓝色</span>表示含7)</p>
<div class="result" id="result"></div>
<div class="stats" id="stats"></div>
<script>
var container = document.getElementById('result');
var count = 0;
for (var n = 100; n <= 999; n++) {
var hundreds = Math.floor(n / 100); // 百位
var tens = Math.floor(n / 10) % 10; // 十位
var ones = n % 10; // 个位
if (hundreds === 3 || hundreds === 7 ||
tens === 3 || tens === 7 ||
ones === 3 || ones === 7) {
var div = document.createElement('div');
div.className = 'num';
// 高亮含3或7的位
var h = (hundreds===3||hundreds===7) ? '<span class="digit-'+(hundreds===3?'3':'7')+'">'+hundreds+'</span>' : hundreds;
var t = (tens===3||tens===7) ? '<span class="digit-'+(tens===3?'3':'7')+'">'+tens+'</span>' : tens;
var o = (ones===3||ones===7) ? '<span class="digit-'+(ones===3?'3':'7')+'">'+ones+'</span>' : ones;
div.innerHTML = h + t + o;
container.appendChild(div);
count++;
}
}
document.getElementById('stats').textContent = '共找到 ' + count + ' 个符合条件的三位数';
</script>
</body>
</html>

📝 代码解释:数字拆分与位判断
核心知识:数字的各位分离算法
① 三位数拆分公式:
js
var n = 237; // 示例数字
var hundreds = Math.floor(n / 100); // 百位:237 ÷ 100 = 2.37 → 向下取整 = 2
var tens = Math.floor(n / 10) % 10; // 十位:237 ÷ 10 = 23 → 23 % 10 = 3
var ones = n % 10; // 个位:237 % 10 = 7
算法推导:
任意三位数 n = abc(a是百位,b是十位,c是个位)
数学表示:n = 100a + 10b + c
提取百位:n ÷ 100 = a.bc → 向下取整 → a
提取十位:n ÷ 10 = ab → ab % 10 → b
提取个位:n % 10 → c
② 典型数字拆分示例:
js
// 示例1:123
hundreds = Math.floor(123 / 100) = Math.floor(1.23) = 1
tens = Math.floor(123 / 10) % 10 = Math.floor(12.3) % 10 = 12 % 10 = 2
ones = 123 % 10 = 3
// 示例2:307
hundreds = Math.floor(307 / 100) = 3
tens = Math.floor(307 / 10) % 10 = 30 % 10 = 0
ones = 307 % 10 = 7
// 示例3:700
hundreds = Math.floor(700 / 100) = 7
tens = Math.floor(700 / 10) % 10 = 70 % 10 = 0
ones = 700 % 10 = 0
③ 条件判断:任一位包含 3 或 7
js
if (hundreds === 3 || hundreds === 7 || // 百位是 3 或 7
tens === 3 || tens === 7 || // 十位是 3 或 7
ones === 3 || ones === 7) { // 个位是 3 或 7
// 满足任一条件,进行处理
}
逻辑展开:
条件1:百位 === 3
条件2:百位 === 7
条件3:十位 === 3
条件4:十位 === 7
条件5:个位 === 3
条件6:个位 === 7
只要满足其中任何一个条件,整个 if 就为 true
④ 动态高亮显示:
js
// 根据每一位是否包含 3 或 7,动态生成 HTML
var h = (hundreds===3||hundreds===7) ?
'<span class="digit-'+(hundreds===3?'3':'7')+'">'+hundreds+'</span>' :
hundreds;
var t = (tens===3||tens===7) ?
'<span class="digit-'+(tens===3?'3':'7')+'">'+tens+'</span>' :
tens;
var o = (ones===3||ones===7) ?
'<span class="digit-'+(ones===3?'3':'7')+'">'+ones+'</span>' :
ones;
div.innerHTML = h + t + o; // 拼接成完整数字
高亮逻辑详解:
js
// 以百位为例
var h;
if (hundreds === 3 || hundreds === 7) {
// 百位是 3 或 7,需要高亮
if (hundreds === 3) {
h = '<span class="digit-3">' + hundreds + '</span>'; // 红色
} else {
h = '<span class="digit-7">' + hundreds + '</span>'; // 蓝色
}
} else {
// 百位不是 3 或 7,普通显示
h = hundreds;
}
⑤ 完整案例演示:
js
// 数字 237 的处理
hundreds = 2 // 不是 3 或 7 → h = 2(普通)
tens = 3 // 是 3 → t = '<span class="digit-3">3</span>'(红色)
ones = 7 // 是 7 → o = '<span class="digit-7">7</span>'(蓝色)
// 最终 HTML:2<span class="digit-3">3</span><span class="digit-7">7</span>
// 显示效果:2³⁷(3是红色,7是蓝色)
// 数字 377 的处理
hundreds = 3 // 是 3 → h = '<span class="digit-3">3</span>'(红色)
tens = 7 // 是 7 → t = '<span class="digit-7">7</span>'(蓝色)
ones = 7 // 是 7 → o = '<span class="digit-7">7</span>'(蓝色)
// 最终 HTML:<span class="digit-3">3</span><span class="digit-7">7</span><span class="digit-7">7</span>
// 显示效果:³⁷⁷(3红色,两个7蓝色)
⑥ 循环范围与计数:
js
var count = 0;
for (var n = 100; n <= 999; n++) { // 遍历所有三位数(100-999)
// 拆分 + 判断 + 创建 DOM
if (/* 符合条件 */) {
count++; // 计数器 +1
}
}
// 最终 count 就是符合条件的三位数总数
关键函数总结:
| 函数 | 作用 | 示例 |
|---|---|---|
| Math.floor() | 向下取整 | Math.floor(2.9) → 2 |
| % 取模 | 取余数 | 23 % 10 → 3 |
| / 除法 | 除法运算 | 237 / 100 → 2.37 |
| .innerHTML | 设置 HTML 内容 | div.innerHTML = '...' |
| 三元运算符 | 条件表达式 | x>10 ? 'big' : 'small' |
数学特性验证:
三位数范围:100 ~ 999(共 900 个)
符合条件的数字特征:
- 百位是 3:300-399(100个)
- 百位是 7:700-799(100个)
- 十位是 3:103,113,123...,每百个有 10 个,共 90 个
- 十位是 7:107,117,127...,每百个有 10 个,共 90 个
- 个位是 3:103,113,123...,每10个有 1 个,共 90 个
- 个位是 7:107,117,127...,每10个有 1 个,共 90 个
注意去重(如 337 同时满足多个条件)
实际应用场景:
- 🔢 数字验证(身份证、银行卡校验位)
- 🎮 游戏逻辑(数字猜谜、密码破解)
- 📊 数据分析(统计特定模式的数字)
- 🎰 彩票系统(数字组合生成与检测)
练习四:100 的阶乘(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>100 的阶乘</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; max-width: 700px; margin: 0 auto; }
.card { background: white; border-radius: 12px; padding: 30px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); }
.result { background: #E8F5E9; border: 2px solid #4CAF50; border-radius: 8px; padding: 20px; margin: 20px 0; word-break: break-all; font-family: monospace; font-size: 0.9em; line-height: 1.8; }
.warning { background: #FFF8E1; border: 1px solid #FFC107; border-radius: 8px; padding: 15px; margin-top: 15px; font-size: 0.9em; }
.steps { margin-top: 20px; }
.step { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid #f0f0f0; font-size: 0.85em; }
.step-num { background: #9C27B0; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 0.75em; flex-shrink: 0; }
code { background: #f5f5f5; padding: 3px 8px; border-radius: 4px; font-family: monospace; }
</style>
</head>
<body>
<div class="card">
<h2>100! 的计算</h2>
<div class="steps" id="steps"></div>
<div class="result" id="result"></div>
<div class="warning">
⚠️ JavaScript 的 Number 类型是 64 位浮点数,最大精确整数约为 2⁵³ ≈ 9×10¹⁵。<br>
100! ≈ 9.33×10¹⁵⁷,远超此范围,因此 JavaScript 会用科学计数法表示(精度有损失)。<br>
在实际项目中,超大整数运算需要使用 <code>BigInt</code> 类型(ES2020+)。
</div>
</div>
<script>
// 方式一:普通 Number(会有精度损失)
var result = 1;
var steps = document.getElementById('steps');
for (var i = 1; i <= 100; i++) {
result *= i;
}
document.getElementById('result').innerHTML =
'<strong>100! = </strong>' + result + '<br><br>' +
'(注意:由于 Number 精度限制,此结果有精度损失,仅供演示)';
// 展示前5步
var r = 1;
for (var i = 1; i <= 5; i++) {
r *= i;
var div = document.createElement('div');
div.className = 'step';
div.innerHTML = '<div class="step-num">' + i + '</div> 1×2×...×' + i + ' = ' + r;
steps.appendChild(div);
}
var dots = document.createElement('div');
dots.className = 'step';
dots.innerHTML = '<div class="step-num">...</div> ...';
steps.appendChild(dots);
</script>
</body>
</html>

📝 代码解释:阶乘计算与精度限制
核心知识:累乘循环 + JavaScript 数字类型限制
① 阶乘定义:
n! = 1 × 2 × 3 × ... × n
示例:
5! = 1 × 2 × 3 × 4 × 5 = 120
10! = 3,628,800
100! ≈ 9.33 × 10¹⁵⁷(天文数字!)
② 基本累乘算法:
js
var result = 1; // 累乘器初始化为 1(乘法单位元)
for (var i = 1; i <= 100; i++) {
result *= i; // result = result * i
}
// 最终 result = 1 × 2 × 3 × ... × 100
执行流程:
第1次迭代:result = 1 × 1 = 1
第2次迭代:result = 1 × 2 = 2
第3次迭代:result = 2 × 3 = 6
第4次迭代:result = 6 × 4 = 24
第5次迭代:result = 24 × 5 = 120
...
第100次迭代:result = (99!) × 100 = 100!
③ JavaScript Number 类型限制:
js
// JavaScript 的 Number 类型特点:
// - 64位双精度浮点数(IEEE 754 标准)
// - 最大安全整数:2⁵³ - 1 ≈ 9×10¹⁵
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
// 超出范围的问题:
var big = 9007199254740992;
console.log(big); // 9007199254740992
console.log(big + 1); // 9007199254740992(❌ 无法精确表示)
console.log(big + 2); // 9007199254740994(✅ 但跳过了 +1)
// 100! 的量级:
// 100! ≈ 9.33×10¹⁵⁷ >> 9×10¹⁵(超出约 142 个数量级!)
④ 科学计数法输出:
js
// JavaScript 自动转为科学计数法
var result = 1;
for (var i = 1; i <= 100; i++) {
result *= i;
}
console.log(result);
// 输出:9.33262154439441e+157
// 表示:9.33262154439441 × 10¹⁵⁷
// 注意:这只是近似值,精度已损失
⑤ 展示前5步(教学目的):
js
var r = 1;
for (var i = 1; i <= 5; i++) {
r *= i; // 累乘
// 创建 DOM 元素显示步骤
var div = document.createElement('div');
div.className = 'step';
div.innerHTML = '<div class="step-num">' + i + '</div> 1×2×...×' + i + ' = ' + r;
steps.appendChild(div);
}
// 输出:
// ① 1×2×...×1 = 1
// ② 1×2×...×2 = 2
// ③ 1×2×...×3 = 6
// ④ 1×2×...×4 = 24
// ⑤ 1×2×...×5 = 120
⑥ BigInt 解决方案(ES2020+):
js
// 使用 BigInt 处理大整数(精确,不会损失精度)
let result = 1n; // 注意:1n 表示 BigInt 类型
for (let i = 1n; i <= 100n; i++) {
result *= i;
}
console.log(result.toString()); // 完整的 158 位数字!
// BigInt 特点:
// ✅ 任意大小的整数
// ✅ 精确计算,无精度损失
// ❌ 不能与 Number 直接混合运算
// ❌ 不能使用 Math 对象方法
⑦ Number vs BigInt 对比:
js
// Number 方式(有精度损失)
var n1 = 1;
for (var i = 1; i <= 100; i++) n1 *= i;
console.log(n1); // 9.33262154439441e+157
// BigInt 方式(精确)
let n2 = 1n;
for (let i = 1n; i <= 100n; i++) n2 *= i;
console.log(n2.toString());
// 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000(完整 158 位)
知识点总结:
| 知识点 | 说明 | 应用 |
|---|---|---|
| 阶乘算法 | 累乘循环 | 组合数学、概率计算 |
| 累乘器 | 初始值为 1 | 乘法累积 |
| Number 限制 | 最大安全整数 2⁵³-1 | 理解精度问题 |
| 科学计数法 | e 表示法 | 表示极大/极小数 |
| BigInt | 任意精度整数 | 大数运算 |
实际应用场景:
- 🧮 组合数学(排列组合 C(n,r) = n! / (r!(n-r)!))
- 🎲 概率计算(古典概型)
- 🔐 密码学(RSA 算法中的大数运算)
- 📊 统计学(分布函数)
⚠️ 注意事项:
1. 小阶乘(≤20!):Number 类型足够
2. 中阶乘(21!~100!):Number 有精度损失,但可表示量级
3. 大阶乘(>100!):必须使用 BigInt 或专门库(如 big.js)
4. Web 应用:考虑浏览器兼容性(BigInt 需 ES2020+)
练习五:求 1! + 2! + ... + 20! 的值(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阶乘求和</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; max-width: 700px; margin: 0 auto; }
table { width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
th { background: #673AB7; color: white; padding: 12px; }
td { padding: 10px 15px; border-bottom: 1px solid #f0f0f0; font-family: monospace; }
tr:nth-child(even) td { background: #F3E5F5; }
.total { background: #673AB7; color: white; font-weight: bold; }
.total td { border: none; }
</style>
</head>
<body>
<h2>1! + 2! + 3! + ... + 20!</h2>
<table>
<tr><th>n</th><th>n!</th><th>累加总和</th></tr>
<tbody id="tbody"></tbody>
</table>
<script>
var tbody = document.getElementById('tbody');
var totalSum = 0; // 累加总和
var factorial = 1; // 当前阶乘值
for (var n = 1; n <= 20; n++) {
factorial *= n; // 第 n 个阶乘 = 上一个阶乘 × n
totalSum += factorial; // 累加
var tr = document.createElement('tr');
tr.innerHTML = '<td>' + n + '</td>' +
'<td>' + factorial.toExponential(3) + '</td>' +
'<td>' + totalSum.toExponential(4) + '</td>';
tbody.appendChild(tr);
}
// 总计行
var totalRow = document.createElement('tr');
totalRow.className = 'total';
totalRow.innerHTML = '<td colspan="2">1! + 2! + ... + 20! =</td>' +
'<td>' + totalSum + '</td>';
tbody.appendChild(totalRow);
</script>
</body>
</html>

📝 代码解释:阶乘求和算法优化
核心知识:双变量累积 + 算法优化
① 问题分析:1! + 2! + 3! + ... + 20!
直观算法(低效):
1! = 1
2! = 1 × 2 = 2
3! = 1 × 2 × 3 = 6
...
每次都从 1 开始重新计算,重复计算量大
优化算法(高效):
1! = 1
2! = 1! × 2 = 1 × 2 = 2
3! = 2! × 3 = 2 × 3 = 6
4! = 3! × 4 = 6 × 4 = 24
...
利用前一次的结果,避免重复计算
② 优化算法实现:
js
var totalSum = 0; // 总和累加器(加法单位元)
var factorial = 1; // 当前阶乘值(乘法单位元)
for (var n = 1; n <= 20; n++) {
factorial *= n; // ① 计算 n!(复用上一次的结果)
totalSum += factorial; // ② 累加到总和
// ...(展示步骤)
}
③ 执行流程详解:
迭代1:factorial = 1 × 1 = 1, totalSum = 0 + 1 = 1
迭代2:factorial = 1 × 2 = 2, totalSum = 1 + 2 = 3
迭代3:factorial = 2 × 3 = 6, totalSum = 3 + 6 = 9
迭代4:factorial = 6 × 4 = 24, totalSum = 9 + 24 = 33
迭代5:factorial = 24 × 5 = 120, totalSum = 33 + 120 = 153
...
迭代20:factorial = 19! × 20 = 20!, totalSum += 20!
④ 算法对比:
js
// ❌ 低效算法(每次重新计算,时间复杂度 O(n²))
var totalSum = 0;
for (var n = 1; n <= 20; n++) {
var factorial = 1;
for (var i = 1; i <= n; i++) { // 内层循环,重复计算
factorial *= i;
}
totalSum += factorial;
}
// 计算量:1 + 2 + 3 + ... + 20 = 210 次乘法
// ✅ 高效算法(累积计算,时间复杂度 O(n))
var totalSum = 0;
var factorial = 1;
for (var n = 1; n <= 20; n++) {
factorial *= n; // 只需 1 次乘法
totalSum += factorial;
}
// 计算量:20 次乘法(节省 190 次!)
⑤ 动态表格渲染:
js
var tr = document.createElement('tr');
tr.innerHTML = '<td>' + n + '</td>' + // n 列
'<td>' + factorial + '</td>' + // n! 列
'<td>' + totalSum + '</td>'; // 累加总和列
tbody.appendChild(tr);
⑥ 表格样式优化:
css
/* 斑马纹效果(提高可读性)*/
tr:nth-child(even) td {
background: #F3E5F5; /* 偶数行浅紫色背景 */
}
/* 总计行特殊样式 */
.total {
background: #673AB7; /* 深紫色背景 */
color: white; /* 白色文字 */
font-weight: bold; /* 加粗 */
}
⑦ 数学验证:
1! + 2! + 3! + 4! + 5!
= 1 + 2 + 6 + 24 + 120
= 153
// JavaScript 验证
var sum = 0, f = 1;
for (var i = 1; i <= 5; i++) {
f *= i;
sum += f;
}
console.log(sum); // 153 ✓
⑧ 知识点总结:
| 知识点 | 说明 | 优势 |
|---|---|---|
| 累积计算 | n! = (n-1)! × n | 避免重复计算 |
| 双变量 | factorial(当前阶乘)+ totalSum(总和) | 同时维护两个状态 |
| 时间复杂度 | O(n) vs O(n²) | 性能提升 10 倍 |
| 动态渲染 | 表格逐行生成 | 实时展示过程 |
| :nth-child | CSS 伪类选择器 | 斑马纹效果 |
算法优化原则:
1. 避免重复计算(缓存中间结果)
2. 利用递推关系(n! = (n-1)! × n)
3. 单次遍历完成(不使用嵌套循环)
4. 空间换时间(用 factorial 变量存储中间值)
实际应用场景:
- 📊 动态报表生成(逐行计算并展示)
- 🧮 累积统计(总销售额、总点击量)
- 📈 滚动窗口计算(移动平均)
- 🎯 递推算法(斐波那契、杨辉三角)
扩展思考:
js
// 如果要计算 1! + 2! + ... + 100!,会怎样?
var totalSum = 0;
var factorial = 1n; // 使用 BigInt 避免精度损失
for (let n = 1n; n <= 100n; n++) {
factorial *= n;
totalSum += factorial;
}
console.log(totalSum.toString());
// 结果是一个超大整数(需要 BigInt)
练习六:水仙花数(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>水仙花数</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: linear-gradient(135deg, #667eea, #764ba2); min-height: 100vh; display: flex; align-items: center; justify-content: center; }
.card { background: white; border-radius: 16px; padding: 40px; max-width: 600px; width: 100%; }
h2 { color: #673AB7; text-align: center; margin-top: 0; }
.definition { background: #F3E5F5; border-radius: 8px; padding: 15px; margin: 15px 0; font-size: 0.9em; line-height: 1.8; }
.flowers { display: flex; flex-wrap: wrap; gap: 15px; justify-content: center; margin-top: 20px; }
.flower { background: linear-gradient(135deg, #667eea, #764ba2); color: white; border-radius: 12px; padding: 20px 25px; text-align: center; box-shadow: 0 4px 15px rgba(102,126,234,0.4); }
.flower .num { font-size: 2.5em; font-weight: bold; }
.flower .formula { font-size: 0.85em; margin-top: 8px; opacity: 0.9; }
</style>
</head>
<body>
<div class="card">
<h2>🌸 水仙花数(自恋数)</h2>
<div class="definition">
<strong>定义:</strong>水仙花数是指一个三位数,它的每个位上的数字的 3 次幂之和等于它本身。<br>
<strong>公式:</strong>abc = a³ + b³ + c³<br>
<strong>范围:</strong>100 ~ 999
</div>
<div class="flowers" id="flowers"></div>
</div>
<script>
var container = document.getElementById('flowers');
// 【循环遍历】所有三位数(100 ~ 999)
for (var n = 100; n <= 999; n++) {
// 【数字拆分】提取各个位上的数字
var a = Math.floor(n / 100); // 百位:n÷100取整
var b = Math.floor(n / 10) % 10; // 十位:n÷10取整后对10取余
var c = n % 10; // 个位:n对10取余
// 【条件判断】检查是否满足水仙花数定义
if (a*a*a + b*b*b + c*c*c === n) {
// 【DOM渲染】找到水仙花数,动态生成展示卡片
var div = document.createElement('div');
div.className = 'flower';
div.innerHTML = '<div class="num">' + n + '</div>' +
'<div class="formula">' + a + '³ + ' + b + '³ + ' + c + '³<br>= ' + (a*a*a) + ' + ' + (b*b*b) + ' + ' + (c*c*c) + '<br>= ' + n + '</div>';
container.appendChild(div);
}
}
</script>
</body>
</html>

📝 算法详解:数字拆分技巧
核心算法:提取各位数字
js
对于三位数 n(例如 153):
百位 a = Math.floor(n / 100) → Math.floor(153/100) = 1
十位 b = Math.floor(n / 10) % 10 → Math.floor(153/10) % 10 = 15 % 10 = 5
个位 c = n % 10 → 153 % 10 = 3
数学原理解释:
| 操作 | 数学含义 | 示例(n=153) | 结果 |
|---|---|---|---|
n / 100 |
移除个位和十位 | 153 / 100 = 1.53 | 1.53 |
Math.floor(n / 100) |
向下取整得百位 | floor(1.53) | 1 |
n / 10 |
移除个位 | 153 / 10 = 15.3 | 15.3 |
Math.floor(n / 10) |
取整 | floor(15.3) | 15 |
% 10 |
取最后一位 | 15 % 10 | 5 |
n % 10 |
直接取个位 | 153 % 10 | 3 |
通用模式:N 位数拆分
js
// 任意位数的数字拆分模板
var n = 12345; // 五位数
var digit1 = n % 10; // 个位:5
var digit2 = Math.floor(n / 10) % 10; // 十位:4
var digit3 = Math.floor(n / 100) % 10; // 百位:3
var digit4 = Math.floor(n / 1000) % 10; // 千位:2
var digit5 = Math.floor(n / 10000) % 10; // 万位:1
// 规律:第 k 位 = Math.floor(n / 10^(k-1)) % 10
🎯 水仙花数特性分析
数学性质:
- 三位数范围:100 ~ 999(共 900 个数)
- 立方和范围 :
- 最小:1³+0³+0³ = 1
- 最大:9³+9³+9³ = 729+729+729 = 2187
- 存在性 :三位水仙花数共有 4 个(153, 370, 371, 407)
验证过程:
js
153: 1³ + 5³ + 3³ = 1 + 125 + 27 = 153 ✓
370: 3³ + 7³ + 0³ = 27 + 343 + 0 = 370 ✓
371: 3³ + 7³ + 1³ = 27 + 343 + 1 = 371 ✓
407: 4³ + 0³ + 7³ = 64 + 0 + 343 = 407 ✓
扩展:其他自恋数(Armstrong Number)
一位数:0-9 都是自恋数(0¹=0, 1¹=1...)
两位数:不存在
三位数:153, 370, 371, 407
四位数:1634, 8208, 9474(如 1634 = 1⁴+6⁴+3⁴+4⁴)
五位数:54748, 92727, 93084
...
🔧 算法优化思路
当前算法:
- 时间复杂度:O(n),n=900(遍历所有三位数)
- 空间复杂度:O(1)
优化方向(不推荐,过度优化):
js
// 剪枝优化:百位为 0 或 1 时,立方和不可能 >= 200
for (var a = 1; a <= 9; a++) {
for (var b = 0; b <= 9; b++) {
for (var c = 0; c <= 9; c++) {
var sum = a*a*a + b*b*b + c*c*c;
var n = a*100 + b*10 + c;
if (sum === n && n >= 100) {
console.log(n);
}
}
}
}
- 虽然遍历次数相同(1000次),但逻辑更清晰
- 适合扩展到 N 位自恋数
💡 知识点总结
| 知识点 | 应用 |
|---|---|
| 数字拆分 | Math.floor(n/10^k) % 10 |
| 幂运算 | a*a*a 或 Math.pow(a,3) |
| 循环遍历 | for 循环逐一检查 |
| 条件筛选 | if 判断符合条件的数 |
| DOM 操作 | 动态生成 HTML 展示结果 |
练习七:100-200 之间所有的素数(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>素数查找器</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.nums { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }
.num { width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: linear-gradient(135deg, #FF6B6B, #FFE66D); font-weight: bold; color: #333; font-size: 0.9em; box-shadow: 0 2px 8px rgba(0,0,0,0.15); }
.stats { background: white; border-radius: 8px; padding: 15px; margin-top: 15px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.algo { background: #E8EAF6; border-radius: 8px; padding: 15px; margin-top: 15px; font-size: 0.9em; line-height: 1.8; }
</style>
</head>
<body>
<h2>100-200 之间所有的素数(质数)</h2>
<div class="algo">
<strong>素数判断算法:</strong>只能被 1 和自身整除的数<br>
<strong>优化技巧:</strong>只需判断到 √n 即可(因为若 n=a×b,a 和 b 中必有一个 ≤ √n)<br>
例如判断 101:只需检查 2 到 √101 ≈ 10,即 2~10 之间是否有因数
</div>
<div class="nums" id="nums"></div>
<div class="stats" id="stats"></div>
<script>
var container = document.getElementById('nums');
var primes = [];
// 【外层循环】遍历范围内所有数字
for (var n = 100; n <= 200; n++) {
var isPrime = true; // 【假设】先假设 n 是素数
// 【内层循环】寻找因数,从 2 判断到 √n
// 关键优化:只需检查到 √n
for (var i = 2; i <= Math.sqrt(n); i++) {
// 【整除判断】如果 n 能被 i 整除
if (n % i === 0) {
isPrime = false; // 找到因数,不是素数
break; // 【提前退出】无需继续检查
}
}
// 【结果处理】如果是素数,添加到结果集并渲染
if (isPrime) {
primes.push(n);
var div = document.createElement('div');
div.className = 'num';
div.textContent = n;
container.appendChild(div);
}
}
document.getElementById('stats').textContent =
'100~200 之间共有 ' + primes.length + ' 个素数';
</script>
</body>
</html>

📝 素数判断算法深度解析
算法核心思想:试除法(Trial Division)
素数定义:只能被 1 和自身整除的数(大于 1)
判断方法:尝试用 2 ~ n-1 之间的所有数去除 n
- 如果都不能整除 → 素数
- 如果存在能整除的 → 合数(非素数)
关键优化:只需检查到 √n
数学原理:
假设 n = a × b,且 a ≤ b
那么 a ≤ √n ≤ b
证明:
如果 a > √n,那么 b = n/a < n/√n = √n
这与 a ≤ b 矛盾
因此,a 和 b 中至少有一个 ≤ √n
结论:
只需检查 2 ~ √n 之间是否有因数
如果这个范围没有因数,那么 √n ~ n 之间也不会有
示例演示:判断 101 是否为素数
js
n = 101
√101 ≈ 10.05
检查过程:
101 % 2 = 1 ✗ 不整除
101 % 3 = 2 ✗ 不整除
101 % 4 = 1 ✗ 不整除
101 % 5 = 1 ✗ 不整除
101 % 6 = 5 ✗ 不整除
101 % 7 = 3 ✗ 不整除
101 % 8 = 5 ✗ 不整除
101 % 9 = 2 ✗ 不整除
101 % 10 = 1 ✗ 不整除
结论:101 是素数
优化效果:
原始方法:需要检查 2~100(99 次)
优化方法:只需检查 2~10(9 次)
性能提升:91% 减少运算次数
🚀 算法性能分析
时间复杂度:
外层循环:O(n),n = 200-100+1 = 101
内层循环:O(√m),m 是当前检查的数
总体复杂度:O(n × √m)
对于 100~200:约 101 × √150 ≈ 1238 次操作
性能对比:
| 方法 | 检查范围 | 101 的检查次数 | 100~200 总检查次数 |
|---|---|---|---|
| 朴素法 | 2 ~ n-1 | 99 | ~10,000 |
| √n 优化 | 2 ~ √n | 9 | ~1,238 |
| 性能提升 | - | 91% | 87.6% |
进一步优化(扩展知识):
- 跳过偶数(除了 2)
js
if (n === 2) return true; // 2 是唯一的偶数素数
if (n % 2 === 0) return false; // 其他偶数都不是素数
// 只检查奇数
for (var i = 3; i <= Math.sqrt(n); i += 2) {
if (n % i === 0) return false;
}
- 减少 50% 的检查次数
- 埃拉托斯特尼筛法(Sieve of Eratosthenes)
js
// 适合批量查找素数(如 1~1000000)
function sieveOfEratosthenes(max) {
var isPrime = new Array(max + 1).fill(true);
isPrime[0] = isPrime[1] = false;
for (var i = 2; i * i <= max; i++) {
if (isPrime[i]) {
// 筛掉 i 的所有倍数
for (var j = i * i; j <= max; j += i) {
isPrime[j] = false;
}
}
}
var primes = [];
for (var i = 2; i <= max; i++) {
if (isPrime[i]) primes.push(i);
}
return primes;
}
- 时间复杂度:O(n log log n)
- 空间复杂度:O(n)
- 适合查找大范围素数
🎯 知识点总结
嵌套循环的 break 使用:
js
for (外层) {
var flag = true;
for (内层) {
if (条件) {
flag = false;
break; // 跳出内层循环
}
}
if (flag) {
// 根据标志变量判断结果
}
}
布尔标志变量模式:
1. 初始假设为 true(isPrime = true)
2. 循环中寻找反例
3. 找到反例立即置 false 并 break
4. 循环结束后根据标志变量判断结果
Math.sqrt() 的使用:
js
Math.sqrt(100) → 10
Math.sqrt(101) → 10.04987562112089
注意:
- sqrt 返回浮点数
- 在循环条件中使用会反复计算
- 优化:var limit = Math.sqrt(n); 提前缓存