JavaScript 从零基础到精通系列:流程控制、函数与作用域

摘要: 程序不是从上到下直线运行的。本篇将全面掌握 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 等)的

    javascript 复制代码
    const colors = ['红', '黄', '蓝'];
    for (const color of colors) {
        console.log(color); // 红, 黄, 蓝
    }
  • for...in:遍历对象的 可枚举属性键 ,常用于对象,但会遍历原型链,使用时小心,通常配合 hasOwnProperty。建议遍历数组时不要用 for...in,因为顺序不保证且会包含非索引属性。

    javascript 复制代码
    const 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 之前只有全局作用域和函数作用域,letconst 带来了块级作用域

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) ,内部变量 secretNumberattempts 不会泄露到全局,这就是利用函数作用域和闭包实现数据私有。

  • Math.random() 生成 0-1 随机数,乘以 100 再取整并加一,得到 1-100。

  • 事件监听使程序响应用户交互,是 Web 开发的核心模式。

  • 通过条件判断给出不同反馈,循环体现在用户可以反复猜,直到猜对。


总结: 本篇我们让代码具备了决策(条件语句)和重复执行(循环)的能力,并学会了如何把逻辑封装进可复用的函数中。作用域和闭包是 JavaScript 的精髓,理解了它们才能迈向中高级。猜数字游戏虽小,但已包含了完整应用的基本骨架:数据结构、业务逻辑、DOM 操作和事件响应。


如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。动手练习是掌握编程最快的方法,请务必亲手敲一遍本文的所有示例代码,并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。

相关推荐
丷丩1 小时前
MapLibre GL JS第28课:PMTiles源和协议
javascript·gis·map·mapbox·maplibre gl js
之歆1 小时前
Day24_JavaScript正则表达式与性能优化实战:从入门到精通
javascript·性能优化·正则表达式
柚子科技2 小时前
Vue3 响应式原理:我被 ref 和 reactive 坑了3次后终于搞懂了
前端·javascript·vue.js
大鱼前端2 小时前
Veaury:让Vue和React组件在同一应用中共存的神器
前端·vue.js·react.js
五月君_2 小时前
继 React、Vue 之后,Three.js 也有 Skills 了!AI 写 3D 终于不“晕”了
javascript·vue.js·人工智能·react.js·3d
scan7242 小时前
大模型只是知道要调用工具,本身不
前端·javascript·html
摇滚侠2 小时前
01 基础语法 JavaScript 入门到精通全套教程
开发语言·javascript·ecmascript
云水一下3 小时前
CSS3从零基础到精通(一):前世今生与基础入门
前端·css3