摘要: 程序不是从上到下直线运行的。本篇将全面掌握 JavaScript 的决策和循环逻辑:if/else、switch、for、while 以及现代的 for...of 循环。随后,我们将深入函数的定义与调用、参数与返回值、箭头函数,并揭开作用域和闭包的神秘面纱。最后,用一场"猜数字"游戏把所学知识串联起来,体验到编程的乐趣与成就感。
一、条件语句:让代码学会做决定
1.1 if / else if / else
这是最直观的决策结构。
javascript
const score = 85;
if (score >= 90) {
console.log('优秀');
} else if (score >= 75) {
console.log('良好');
} else if (score >= 60) {
console.log('及格');
} else {
console.log('需要加油哦');
}
// 输出:良好

条件表达式会被隐式转换为布尔值,所以我们可以利用真假值:
javascript
const userInput = ''; // 空字符串为 falsy
if (userInput) {
console.log('输入有效');
} else {
console.log('输入不能为空');
}

1.2 三元运算符 ? :
对于简单的条件赋值,可以用更简洁的三元运算符:
javascript
const age = 20;
const canVote = (age >= 18) ? '可以投票' : '还不能投票';
console.log(canVote); // 可以投票

它相当于 if...else 的简写,但不可嵌套太深,以免降低可读性。
1.3 switch 语句
当有多个确定值要判断时,switch 比多条 else if 更清晰:
javascript
const day = 3;
let dayName;
switch (day) {
case 1:
dayName = '星期一';
break;
case 2:
dayName = '星期二';
break;
case 3:
dayName = '星期三';
break;
case 4:
dayName = '星期四';
break;
case 5:
dayName = '星期五';
break;
case 6:
case 7:
dayName = '周末';
break;
default:
dayName = '未知';
}
console.log(dayName); // 星期三

特别提醒 :每个 case 末尾的 break 至关重要,如果忘记写,会发生 "贯穿" (fall-through) ,即会继续执行下一个 case 的代码。有时我们刻意利用贯穿,但绝大部分时候要记得加 break。
二、循环:重复工作的自动化
循环就是反复执行同一段代码,直到条件不满足为止。
2.1 for 循环
最经典的循环,通常用于已知次数。
javascript
// 打印 0 到 4
for (let i = 0; i < 5; i++) {
console.log(`当前 i 的值为:${i}`);
}
// 初始化表达式:let i = 0 只执行一次
// 条件表达式:i < 5 每次循环前检查
// 更新表达式:i++ 每次循环结束后执行
//for 循环常用于遍历数组:
const fruits = ['苹果', '香蕉', '橘子'];
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}

2.2 while 与 do...while
当不确定循环次数时,可用 while。
javascript
// 持续掷骰子直到掷出 6
let dice = 0;
while (dice !== 6) {
dice = Math.ceil(Math.random() * 6);
console.log(`掷出了 ${dice}`);
}
console.log('终于掷出6了!');

do...while 保证循环体至少执行一次,因为条件判断在末尾。
javascript
let input;
do {
input = prompt('请输入退出'); // 浏览器环境
} while (input !== '退出');

2.3 for...of 与 for...in
ES6 带来了更简洁的遍历方式。
-
for...of:遍历可迭代对象(数组、字符串、Map、Set 等)的 值。javascriptconst colors = ['红', '黄', '蓝']; for (const color of colors) { console.log(color); // 红, 黄, 蓝 }
-
for...in:遍历对象的 可枚举属性键 ,常用于对象,但会遍历原型链,使用时小心,通常配合hasOwnProperty。建议遍历数组时不要用for...in,因为顺序不保证且会包含非索引属性。javascriptconst person = { name: '小明', age: 18 }; for (const key in person) { console.log(`${key}: ${person[key]}`); }
2.4 跳出循环:break 与 continue
-
break:彻底终止整个循环。 -
continue:跳过本次循环的剩余部分,直接进入下一次迭代。
javascript
for (let i = 0; i < 10; i++) {
if (i === 3) continue; // 跳过 3
if (i === 7) break; // 在 7 处终止
console.log(i); // 0,1,2,4,5,6
}

三、函数:封装可复用的逻辑块
函数就是一组可重复调用的代码。DRY 原则(Don't Repeat Yourself)的核心工具。
3.1 函数声明与调用
javascript
// 定义函数
function greet(name) {
return `你好,${name}!`;
}
// 调用函数
const message = greet('小白');
console.log(message); // 你好,小白!

3.2 参数与返回值
函数可以有多个参数,也可以没有。return 语句将值返回给调用者,并立刻结束函数执行。
javascript
function add(a, b) {
return a + b;
console.log('这行不会执行');
}
ES6 引入了默认参数:
javascript
function power(base, exponent = 2) {
return base ** exponent;
}
console.log(power(5)); // 25 (平方)
console.log(power(5, 3)); // 125 (立方)

3.3 函数表达式与箭头函数
函数在 JavaScript 中是一等公民,可以赋值给变量。
javascript
// 匿名函数表达式
const sayHi = function(name) {
return `Hi, ${name}`;
};
// 箭头函数 (ES6)
const sayHello = (name) => {
return `Hello, ${name}`;
};
// 更简洁的写法:只有一个参数可省略括号,函数体只有一句 return 可省略花括号和 return
const greeting = name => `Hey, ${name}`;
console.log(greeting('世界')); // Hey, 世界

箭头函数不只是写法简洁,它没有自己的 this,会捕获外层上下文的 this,这在 DOM 事件和对象方法中有重要影响,我们后续详谈。
四、作用域与闭包
4.1 作用域类型
作用域决定了变量的可访问范围。ES6 之前只有全局作用域和函数作用域,let 和 const 带来了块级作用域。
javascript
// 全局作用域
let globalVar = '我是全局的';
function testScope() {
// 函数作用域
let funcVar = '我在函数里';
if (true) {
// 块级作用域
let blockVar = '我在代码块里';
var notBlock = '我是 var,无视块';
console.log(blockVar); // 可以访问
}
console.log(notBlock); // 可以访问,因为 var 不识别块作用域
// console.log(blockVar); // ❌ 报错,blockVar 在块外不可见
}
作用域链:内部作用域能访问外部作用域的变量,反之不行。这像一层层嵌套的盒子,内层可以看外层。
4.2 闭包 (Closure)
闭包是 JavaScript 最强大的特性之一。当函数可以记住并访问它被创建时的词法作用域,即使这个函数在其他地方被调用,就产生了闭包。
javascript
function createCounter() {
let count = 0; // 这个变量被内部函数引用,形成闭包
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 1
counter(); // 2
// counter 函数依然可以访问和修改 count,但外部无法直接触碰 count

闭包常用于:数据私有化、创建模块、事件处理函数中保留循环变量等。我们会在实战项目中反复使用。
五、实战项目:猜数字游戏
让我们把条件、循环、函数和作用域结合起来,编写一个完整的浏览器猜数字游戏。
需求:程序随机生成 1-100 之间的整数,用户在输入框猜数字,页面给出"大了"、"小了"或"猜对了"的反馈,并记录猜的次数。
HTML 结构 (guess.html):
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>猜数字游戏</title>
</head>
<body>
<h2>🎯 猜数字 (1-100)</h2>
<input type="number" id="guessInput" placeholder="输入你的猜测">
<button id="submitBtn">猜!</button>
<p id="message"></p>
<p id="attempts">已猜次数:0</p>
<script src="guess.js"></script>
</body>
</html>
JavaScript (guess.js):
javascript
// 立即执行函数创建一个私有作用域,避免全局污染
(function() {
// 私有变量
const secretNumber = Math.floor(Math.random() * 100) + 1;
let attempts = 0;
// 获取 DOM 元素
const guessInput = document.getElementById('guessInput');
const submitBtn = document.getElementById('submitBtn');
const messageEl = document.getElementById('message');
const attemptsEl = document.getElementById('attempts');
// 核心游戏逻辑函数
function checkGuess() {
const userGuess = Number(guessInput.value);
if (!userGuess || userGuess < 1 || userGuess > 100) {
messageEl.textContent = '⚠️ 请输入 1~100 之间的有效数字';
return;
}
attempts++;
attemptsEl.textContent = `已猜次数:${attempts}`;
if (userGuess === secretNumber) {
messageEl.textContent = `🎉 恭喜你!答案就是 ${secretNumber},你用了 ${attempts} 次猜对!`;
// 猜对后禁止继续
submitBtn.disabled = true;
guessInput.disabled = true;
} else if (userGuess > secretNumber) {
messageEl.textContent = '📈 大了!再试试。';
} else {
messageEl.textContent = '📉 小了!再试试。';
}
guessInput.value = '';
guessInput.focus();
}
// 绑定事件
submitBtn.addEventListener('click', checkGuess);
guessInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
checkGuess();
}
});
})();

解析:
-
外层
(function(){...})()是一个 立即执行函数表达式 (IIFE) ,内部变量secretNumber和attempts不会泄露到全局,这就是利用函数作用域和闭包实现数据私有。 -
Math.random()生成 0-1 随机数,乘以 100 再取整并加一,得到 1-100。 -
事件监听使程序响应用户交互,是 Web 开发的核心模式。
-
通过条件判断给出不同反馈,循环体现在用户可以反复猜,直到猜对。
总结: 本篇我们让代码具备了决策(条件语句)和重复执行(循环)的能力,并学会了如何把逻辑封装进可复用的函数中。作用域和闭包是 JavaScript 的精髓,理解了它们才能迈向中高级。猜数字游戏虽小,但已包含了完整应用的基本骨架:数据结构、业务逻辑、DOM 操作和事件响应。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。动手练习是掌握编程最快的方法,请务必亲手敲一遍本文的所有示例代码,并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。