跳转至

第 4 章:JavaScript 基础语法——让网页"活"起来

📺 本章概览

项目 内容
学习目标 掌握 JavaScript 核心语法,理解变量、函数、数组和对象,能阅读和理解网页中的 JS 代码逻辑
核心比喻 家电控制系统(智能开关) —— JavaScript 让网页"活"起来,响应用户操作
预计时长 120 分钟
关键概念 变量声明、数据类型、运算符、条件判断、循环、函数、数组方法、对象
实践任务 阅读并分析一个计算器的 JavaScript 代码,理解每行代码的执行逻辑

HTML 定义了网页的 结构,CSS 定义了网页的 外观,而 JavaScript 定义了网页的 行为。如果把网页比作一栋智能房子:HTML 是钢筋水泥,CSS 是室内装修,JavaScript 就是 智能家居系统——它让灯光自动开关、空调自动调温、门锁自动识别。没有 JavaScript,网页就只是一张静态的"图片"。


4.1 JavaScript 是什么?—— 认识网页的"大脑"

4.1.1 JavaScript 的定位

JavaScript(简称 JS)是一门 真正的编程语言。与 HTML 和 CSS 不同,JS 拥有完整的编程能力:

  • 变量:存储和操作数据
  • 逻辑判断:根据条件执行不同代码(if-else)
  • 循环:重复执行某段代码(for/while)
  • 函数:封装可复用的代码块
  • 对象:组织复杂的数据结构

Java 和 JavaScript 的关系

很多人以为 JavaScript 是 Java 的"简化版",这是一个经典的误解。Java 和 JavaScript 的关系就像"雷锋"和"雷峰塔"——名字相似,但完全是两门不同的语言。JavaScript 最初叫 LiveScript,后来为了蹭 Java 的热度才改名。

4.1.2 JavaScript 的三种引入方式

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>JavaScript 引入方式</title>

    <!-- 方式一:外部脚本文件(推荐) -->
    <script src="script.js" defer></script>

    <!-- 方式二:内部脚本 -->
    <script>
        console.log('Hello from internal script!');
    </script>
</head>
<body>
    <!-- 方式三:行内事件处理(不推荐) -->
    <button onclick="alert('Hello!')">点击我</button>
</body>
</html>

代码解读:

方式 写法 推荐度 说明
外部文件 <script src="../script.js" defer> ⭐⭐⭐⭐⭐ 结构与行为分离,可缓存,defer 确保 DOM 加载完再执行
内部脚本 <script>...</script> ⭐⭐⭐ 适合小型演示,但不利于维护
行内事件 onclick="..." 与 HTML 耦合,难以维护,不符合关注点分离原则

defer vs async

  • defer:脚本在后台下载,等 HTML 解析完成后 按顺序执行(推荐)。
  • async:脚本在后台下载,下载完立即执行,不保证执行顺序。
  • 不加属性:脚本下载和执行都会 阻塞 HTML 解析(不推荐)。

4.2 变量与数据类型

4.2.1 声明变量

// 现代写法(ES6+):使用 let 和 const
let name = '张三';        // 可变变量
const PI = 3.14159;       // 常量,不可重新赋值
let age = 25;
age = 26;                 // ✅ let 可以修改
// PI = 3.14;             // ❌ const 不能修改,会报错

// 过时写法:var(有作用域问题,不推荐)
var oldWay = '不推荐使用';

代码解读:

关键字 可修改 作用域 推荐度
const 块级 ⭐⭐⭐⭐⭐ 默认首选
let 块级 ⭐⭐⭐⭐ 需要修改时使用
var 函数级 ⭐ 遗留代码中常见,新代码避免

默认使用 const,需要修改时改用 let

这是一个最佳实践:声明变量时优先使用 const,当你确定这个变量需要被重新赋值时,再改为 let。这样做可以减少意外的变量修改。

4.2.2 基本数据类型

JavaScript 有 7 种基本数据类型:

// 1. 字符串(String)
const name = '张三';
const greeting = "你好,世界!";
const template = `欢迎你,${name}!`;  // 模板字符串(ES6),支持变量插值

// 2. 数字(Number)—— 不区分整数和浮点数
const age = 25;
const price = 19.99;
const infinity = Infinity;
const notANumber = NaN;  // Not a Number,如 0/0 的结果

// 3. 布尔值(Boolean)
const isLoggedIn = true;
const isExpired = false;

// 4. 未定义(Undefined)—— 变量已声明但未赋值
let something;
console.log(something);  // undefined

// 5. 空值(Null)—— 表示"空"或"不存在"
const empty = null;

// 6. 符号(Symbol,ES6)—— 创建唯一标识
const uniqueId = Symbol('id');

// 7. 大整数(BigInt,ES2020)—— 表示超大整数
const hugeNumber = 9007199254740991n;

4.2.3 类型检测

console.log(typeof 'hello');     // "string"
console.log(typeof 42);          // "number"
console.log(typeof true);        // "boolean"
console.log(typeof undefined);   // "undefined"
console.log(typeof null);        // "object"(这是一个历史遗留 bug)
console.log(typeof Symbol());    // "symbol"
console.log(typeof function(){});// "function"

4.3 运算符

4.3.1 算术运算符

const a = 10;
const b = 3;

console.log(a + b);   // 13  加法
console.log(a - b);   // 7   减法
console.log(a * b);   // 30  乘法
console.log(a / b);   // 3.333...  除法
console.log(a % b);   // 1   取余(模运算)
console.log(a ** b);  // 1000  指数(ES7)

4.3.2 比较运算符

console.log(5 > 3);          // true
console.log(5 >= 5);         // true
console.log(5 < 3);          // false
console.log(5 === 5);        // true  严格相等(比较值和类型)
console.log(5 === '5');      // false 类型不同
console.log(5 == '5');       // true  宽松相等(会做类型转换,不推荐)
console.log(5 !== '5');      // true  严格不等

始终使用 === 而非 ==

== 会进行隐式类型转换,导致意想不到的结果:

console.log(0 == '');       // true(诡异!)
console.log(0 == false);    // true(诡异!)
console.log(null == undefined); // true
使用 ===(严格相等)可以避免这些陷阱。

4.3.3 逻辑运算符

// 与(AND):两边都为 true 才返回 true
console.log(true && true);    // true
console.log(true && false);   // false

// 或(OR):任意一边为 true 就返回 true
console.log(true || false);   // true
console.log(false || false);  // false

// 非(NOT):取反
console.log(!true);           // false
console.log(!false);          // true

// 短路求值(常用技巧)
const displayName = userName || '匿名用户';  // 如果 userName 为空,使用默认值
const result = isLoggedIn && getUserData();  // 只有登录了才调用函数

// 空值合并运算符(ES2020)
const name = inputName ?? '默认名称';  // 只有 null/undefined 时才用默认值

4.4 条件判断——让代码做选择

4.4.1 if-else 语句

const score = 85;

if (score >= 90) {
    console.log('优秀');
} else if (score >= 80) {
    console.log('良好');
} else if (score >= 60) {
    console.log('及格');
} else {
    console.log('不及格');
}
// 输出:良好

4.4.2 switch 语句

const day = '星期一';

switch (day) {
    case '星期一':
        console.log('新的一周开始了!');
        break;
    case '星期五':
        console.log('马上周末了!');
        break;
    case '星期六':
    case '星期日':
        console.log('周末愉快!');
        break;
    default:
        console.log('工作日继续加油!');
}

4.4.3 三元运算符

const age = 20;
const canVote = age >= 18 ? '可以投票' : '不能投票';
console.log(canVote);  // 可以投票

// 等价于:
// if (age >= 18) {
//     canVote = '可以投票';
// } else {
//     canVote = '不能投票';
// }

4.5 循环——让代码重复执行

4.5.1 for 循环

// 经典 for 循环
for (let i = 0; i < 5; i++) {
    console.log(`第 ${i + 1} 次循环`);
}

// 遍历数组
const fruits = ['苹果', '香蕉', '橘子', '葡萄'];
for (let i = 0; i < fruits.length; i++) {
    console.log(fruits[i]);
}

4.5.2 for...of 循环(推荐遍历数组)

const colors = ['红色', '绿色', '蓝色'];

for (const color of colors) {
    console.log(color);
}
// 输出:红色 绿色 蓝色

4.5.3 while 循环

let count = 0;

while (count < 3) {
    console.log(`计数:${count}`);
    count++;
}
// 输出:计数:0  计数:1  计数:2

4.5.4 循环控制

// break:立即退出循环
for (let i = 0; i < 10; i++) {
    if (i === 5) break;
    console.log(i);  // 输出 0 1 2 3 4
}

// continue:跳过当前迭代,继续下一次
for (let i = 0; i < 5; i++) {
    if (i === 2) continue;
    console.log(i);  // 输出 0 1 3 4
}

4.6 函数——封装可复用的代码

4.6.1 函数声明

// 函数声明
function greet(name) {
    return `你好,${name}!`;
}

console.log(greet('张三'));  // 你好,张三!

// 函数表达式
const add = function(a, b) {
    return a + b;
};

console.log(add(3, 5));  // 8

4.6.2 箭头函数(ES6,推荐)

// 完整写法
const multiply = (a, b) => {
    return a * b;
};

// 简写:只有一条 return 语句时,可省略 {} 和 return
const multiplyShort = (a, b) => a * b;

// 只有一个参数时,可省略 ()
const square = x => x * x;

// 没有参数时,必须保留 ()
const sayHello = () => console.log('Hello!');

4.6.3 参数默认值

function createUser(name, role = '普通用户') {
    return { name, role };
}

console.log(createUser('张三'));           // { name: '张三', role: '普通用户' }
console.log(createUser('李四', '管理员'));  // { name: '李四', role: '管理员' }

4.7 数组——有序的数据集合

4.7.1 创建和访问数组

// 创建数组
const fruits = ['苹果', '香蕉', '橘子'];
const numbers = [1, 2, 3, 4, 5];
const mixed = ['文字', 42, true, null];  // 数组可以包含不同类型

// 访问元素(索引从 0 开始)
console.log(fruits[0]);     // 苹果
console.log(fruits[1]);     // 香蕉
console.log(fruits.length); // 3

// 修改元素
fruits[1] = '葡萄';
console.log(fruits);  // ['苹果', '葡萄', '橘子']

4.7.2 常用数组方法

const arr = [1, 2, 3, 4, 5];

// 末尾添加/删除
arr.push(6);        // [1, 2, 3, 4, 5, 6]
arr.pop();          // [1, 2, 3, 4, 5]  返回被删除的 6

// 开头添加/删除
arr.unshift(0);     // [0, 1, 2, 3, 4, 5]
arr.shift();        // [1, 2, 3, 4, 5]  返回被删除的 0

// 查找元素
console.log(arr.indexOf(3));     // 2(索引位置)
console.log(arr.includes(3));    // true(是否存在)

// 截取子数组(不修改原数组)
const sub = arr.slice(1, 3);     // [2, 3]

// 删除/插入元素(修改原数组)
arr.splice(2, 1);                // 从索引 2 开始删除 1 个元素 → [1, 2, 4, 5]
arr.splice(2, 0, 99);            // 在索引 2 处插入 99 → [1, 2, 99, 4, 5]

4.7.3 数组遍历方法(函数式编程)

const numbers = [1, 2, 3, 4, 5];

// forEach:遍历每个元素
numbers.forEach((num, index) => {
    console.log(`索引 ${index}${num}`);
});

// map:将每个元素映射为新值,返回新数组
const doubled = numbers.map(num => num * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]

// filter:筛选符合条件的元素,返回新数组
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens);  // [2, 4]

// find:查找第一个符合条件的元素
const found = numbers.find(num => num > 3);
console.log(found);  // 4

// reduce:将数组归约为单个值
const sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum);  // 15

4.8 对象——无序的键值对集合

4.8.1 创建和访问对象

// 创建对象
const user = {
    name: '张三',
    age: 25,
    email: 'zhangsan@example.com',
    isAdmin: false,
    hobbies: ['编程', '阅读', '跑步'],
    address: {
        city: '北京',
        district: '海淀区'
    }
};

// 访问属性
console.log(user.name);           // 张三(点号访问)
console.log(user['email']);       // zhangsan@example.com(方括号访问)
console.log(user.hobbies[0]);     // 编程
console.log(user.address.city);   // 北京(链式访问)

// 修改和添加属性
user.age = 26;
user.phone = '13800138000';       // 添加新属性

// 删除属性
delete user.isAdmin;

4.8.2 对象方法简写(ES6)

const calculator = {
    // 方法简写
    add(a, b) {
        return a + b;
    },

    subtract(a, b) {
        return a - b;
    },

    // 属性值简写
    display(result) {
        console.log(`计算结果:${result}`);
    }
};

console.log(calculator.add(10, 5));  // 15

4.8.3 解构赋值(ES6)

const user = { name: '张三', age: 25, email: 'zhangsan@example.com' };

// 对象解构
const { name, age, email } = user;
console.log(name, age);  // 张三 25

// 重命名
const { name: userName, age: userAge } = user;
console.log(userName);  // 张三

// 默认值
const { role = '普通用户' } = user;
console.log(role);  // 普通用户

// 数组解构
const colors = ['红色', '绿色', '蓝色'];
const [first, second, third] = colors;
console.log(first, second);  // 红色 绿色

4.9 实践任务:阅读并分析计算器代码

任务要求

下面是一个完整的网页计算器代码(HTML + CSS + JS)。你的任务是 逐行阅读 JavaScript 部分,理解每行代码的执行逻辑,回答以下问题:

  • parseFloat() 函数的作用是什么?为什么不用 parseInt()
  • isNaN() 检查的是什么情况?如果用户不输入任何内容就点击按钮会发生什么?
  • switch 语句中每个 case 分支做了什么?break 的作用是什么?
  • 除法运算中,为什么需要单独检查 num2 === 0
  • resultElement.textContentresultElement.style.color 分别修改了什么?

参考代码

<!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 {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: #f0f2f5;
            font-family: "Microsoft YaHei", sans-serif;
        }
        .calculator {
            background: white;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
            width: 320px;
        }
        .calculator h2 {
            text-align: center;
            color: #2c3e50;
            margin-bottom: 20px;
        }
        .calculator input {
            width: 100%;
            padding: 10px;
            margin-bottom: 12px;
            border: 1px solid #ddd;
            border-radius: 6px;
            font-size: 16px;
            box-sizing: border-box;
        }
        .buttons {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 8px;
            margin-bottom: 15px;
        }
        .buttons button {
            padding: 12px;
            border: none;
            border-radius: 6px;
            font-size: 18px;
            cursor: pointer;
            background: #4A90D9;
            color: white;
            transition: background 0.3s ease;
        }
        .buttons button:hover {
            background: #357ABD;
        }
        .result {
            text-align: center;
            font-size: 20px;
            font-weight: bold;
            color: #2c3e50;
            padding: 10px;
            background: #f8f9fa;
            border-radius: 6px;
            min-height: 30px;
        }
    </style>
</head>
<body>
    <div class="calculator">
        <h2>简易计算器</h2>
        <input type="number" id="num1" placeholder="请输入第一个数字">
        <input type="number" id="num2" placeholder="请输入第二个数字">
        <div class="buttons">
            <button onclick="calculate('+')">+</button>
            <button onclick="calculate('-')">-</button>
            <button onclick="calculate('*')">×</button>
            <button onclick="calculate('/')">÷</button>
        </div>
        <div class="result" id="result">结果将显示在这里</div>
    </div>

    <script>
        function calculate(operator) {
            const num1 = parseFloat(document.getElementById('num1').value);
            const num2 = parseFloat(document.getElementById('num2').value);
            const resultElement = document.getElementById('result');

            if (isNaN(num1) || isNaN(num2)) {
                resultElement.textContent = '请输入有效的数字!';
                resultElement.style.color = '#e74c3c';
                return;
            }

            let result;
            switch (operator) {
                case '+':
                    result = num1 + num2;
                    break;
                case '-':
                    result = num1 - num2;
                    break;
                case '*':
                    result = num1 * num2;
                    break;
                case '/':
                    if (num2 === 0) {
                        resultElement.textContent = '错误:除数不能为 0!';
                        resultElement.style.color = '#e74c3c';
                        return;
                    }
                    result = num1 / num2;
                    break;
            }

            resultElement.textContent = `${num1} ${operator} ${num2} = ${result}`;
            resultElement.style.color = '#2c3e50';
        }
    </script>
</body>
</html>

验证步骤

  1. 将代码保存为 calculator.html,用浏览器打开。
  2. 输入两个数字,分别点击加减乘除按钮,验证结果是否正确——同时观察 resultElement.textContent 的变化。
  3. 输入 0 作为第二个数字,点击除法,验证是否显示红色错误提示——注意 resultElement.style.color 的变化。
  4. 不输入数字直接点击按钮,验证是否提示"请输入有效的数字"——理解 isNaN() 的检查逻辑。
  5. 按 F12 打开 Console,手动输入 calculate('+') 测试函数调用。

📋 本章要点总结

  • 理解 JavaScript 是真正的编程语言,负责网页的交互行为
  • 掌握 constlet 声明变量,避免使用 var
  • 熟悉 7 种基本数据类型:string、number、boolean、undefined、null、symbol、bigint
  • 理解 ===(严格相等)和 ==(宽松相等)的区别,始终使用 ===
  • 掌握 if-else 条件判断和三元运算符
  • 熟练使用 for 循环和 for...of 循环遍历数组
  • 能声明和调用函数,理解参数和返回值
  • 会使用箭头函数 () => {} 简化代码
  • 掌握数组的常用方法:push、pop、map、filter、find、reduce
  • 理解对象的概念,能创建和访问对象属性
  • 会使用解构赋值简化代码

📚 课后练习

基础练习

  1. 编写一个函数 isEven(n),判断一个数字是否为偶数,返回布尔值。
  2. 编写一个函数 getMax(arr),接收一个数字数组,返回其中的最大值。
  3. 创建一个对象 book,包含属性:title、author、year、pages,并编写一个方法 getSummary() 返回书籍简介。

进阶挑战

  1. 编写一个函数 fizzBuzz(n):遍历 1 到 n,能被 3 整除输出 "Fizz",能被 5 整除输出 "Buzz",能同时被 3 和 5 整除输出 "FizzBuzz",否则输出数字本身。
  2. 扩展计算器:添加"清空"按钮、支持小数运算、美化结果显示。

👉 进入第 5 章:DOM 操作与事件处理 →