WEB 前端编程核心 · 大学一年级

掌握 JavaScript:让网页真正"活"起来

从最基础的变量、函数、对象,到 DOM 操控、事件响应、Promise 异步、Fetch 网络请求,最后完成 5 个真实可运行的项目。 本教程与本系列前序课程(C → C++ → Java → Python → SQL/数据库)保持一致的节奏与难度梯度。

🟡 43 章正文 📦 5 个综合项目 ❓ 12 道课堂训练 🎯 大一新生起步 🌐 纯前端 · 离线可读
基础语法 函数与 OOP DOM + 事件 BOM + 异步 Fetch + JSON 5 个项目

学习指南:为什么、怎么学、学到什么

JavaScript(简称 JS)是 Web 前端编程的核心语言。它负责把 HTML(结构)和 CSS(样式)组合的网页变成可交互的应用——按钮点击、表单验证、动画、数据请求……都离不开它。

0.1 三大 Web 技术的关系

💡 三层结构
HTML   →   网页结构(骨架)
CSS    →   网页样式(外观)
JS     →   网页行为(交互、逻辑、数据)

0.2 与 Java 的关键区别(大一常见误解)

维度JavaJavaScript
类型强类型 / 编译型动态类型 / 解释型
运行JVM 虚拟机浏览器 或 Node.js
主要场景后端服务、Android网页交互、Node 后端
类与继承基于 class基于原型(ES6 后可写 class)
线程模型多线程单线程 + 事件循环

0.3 你将学到(按主线)

  1. 语法基础:变量、数据类型、运算符、流程控制、函数
  2. 核心数据结构:字符串、数组、对象、Map/Set、解构
  3. 面向对象:class、继承、封装、原型链
  4. DOM 操控:查找元素、修改内容与样式、创建/删除节点
  5. 事件:addEventListener、事件对象、冒泡与委托
  6. BOM + 异步:定时器、Promise、async/await、Event Loop
  7. 网络:Fetch、HTTP 基础、JSON
  8. 工程化:模块化、调试、npm 与 Node.js 入门
  9. 5 个真实项目:计算器 / Todo / 成绩管理 / 天气 / 图书
衔接关系:学完本教程可与本系列 SQL/数据库 课程合流,自然过渡到 Node.js 全栈开发。

运行环境:浏览器、DevTools、VS Code ⭐⭐⭐

1.1 在浏览器中直接运行

最简单的方式:在 HTML 里嵌入 <script>

HTML
<!-- index.html -->
<script>
    console.log("Hello JavaScript");
</script>

打开 DevTools(按 F12 或右键 → 检查),切到 Console 面板看输出。

1.2 三种引入方式

方式用法适用
行内<button onclick="alert(1)">极简 demo
内部<script>...</script>小练习
外部<script src="app.js"></script>真实项目(推荐)

1.3 DevTools 五大面板

🔍 必学面板
  • Console:运行 JS、查看日志与错误
  • Elements:查看/调试 HTML 与 CSS
  • Sources:打断点调试 JS
  • Network:查看网络请求(Fetch/XHR)
  • Application:查看 localStorage / Cookie

1.4 VS Code 工作流

步骤
① 创建 index.html,写入基础 HTML 骨架
② 创建 app.js,写 console.log("hi");
③ 在 HTML 里通过 <script src="app.js"></script> 引入
④ 安装扩展 Live Server,右键 "Open with Live Server"
⑤ 修改代码自动刷新
推荐扩展:Live Server、Prettier、ESLint、JavaScript (ES6) code snippets。

基本语法 ⭐⭐⭐

2.1 注释

JavaScript
// 单行注释

/*
 多行注释
 常用于函数说明
*/

function greet(name) {
    // 内部注释
    return `你好,${name}`;
}

2.2 语句与分号

JavaScript
let x = 10;            // 推荐加分号
let y = 20             // 不加分号 ASI 会自动插入(不推荐依赖)
console.log(x + y);

2.3 大小写敏感

⚠️ 大小写
let myname = "Alice";    ✓
let myName = "Alice";    ✗ 是另一个变量

2.4 标识符命名规则

2.5 常用关键字清单

类别常见关键字
声明var let const function class
流程if else switch for while do break continue return
异常try catch finally throw
异步async await
逻辑true false null undefined
对象this super new delete typeof instanceof
ES6+import export yield

变量:let / const / var ⭐⭐⭐

现代 JavaScript 以 letconst 为主,var 仅在维护老代码时见到。

3.1 三种声明方式对比

var(ES5)let(ES6)const(ES6)
作用域函数作用域块级作用域块级作用域
可重新赋值❌(不可改绑定)
重复声明✅(不报错)❌(报错)❌(报错)
提升提升且初始化为 undefined提升但有 TDZ提升但有 TDZ
推荐度❌ 老旧✅ 会变的用✅ 默认就用

3.2 实战写法

JavaScript
const PI = 3.14159;          // 常量:不会变
let age  = 18;               // 变量:可能会改
const name = "张三";         // 默认常量优先

// const 对象的内部属性仍可改(绑定不可变)
const user = { name: "Alice" };
user.name = "Bob";        // ✅ OK
user = {};                       // ❌ TypeError

3.3 块级作用域

JavaScript
if (true) {
    let x = 10;
    var y = 20;
}
console.log(x);  // ❌ ReferenceError: x is not defined
console.log(y);  // ✅ 20(var 泄漏到外层)

数据类型 ⭐⭐⭐

4.1 基本类型(7 种 Primitive)

类型示例说明
Number42, 3.14, NaN不区分整型与浮点型
String"hi", 'hi', \`hi\`三种引号均可
Booleantrue, false逻辑判断
Undefinedundefined变量已声明但未赋值
Nullnull主动表示"空"
BigInt9007199254740993n超大整数
SymbolSymbol('id')唯一标识符

4.2 引用类型(Object)

包括 ObjectArrayFunctionDateRegExpMapSet 等。基本类型存"值",引用类型存"地址"。

4.3 typeof 检测

JavaScript
console.log(typeof 42);           // "number"
console.log(typeof "hi");         // "string"
console.log(typeof true);         // "boolean"
console.log(typeof undefined);    // "undefined"
console.log(typeof null);          // "object" ← 历史遗留 bug!
console.log(typeof [1,2,3]);        // "object"
console.log(typeof function(){});  // "function"

4.4 基本 vs 引用(关键)

JavaScript
// 基本类型:拷贝值
let a = 10;
let b = a;
b = 20;
console.log(a);   // 10(a 没受影响)

// 引用类型:拷贝地址
const arr1 = [1, 2];
const arr2 = arr1;
arr2.push(3);
console.log(arr1);  // [1, 2, 3] ← 注意!

类型转换 ⭐⭐⭐

5.1 显式转换

JavaScript
Number("123");      // 123
Number("abc");      // NaN (Not a Number)
Number(true);       // 1
Number(null);       // 0
Number(undefined);  // NaN

String(123);       // "123"
String(true);       // "true"

Boolean(0);         // false
Boolean("");        // false
Boolean("hi");      // true
Boolean(null);      // false

5.2 隐式转换(容易踩坑)

JavaScript
console.log("10" + 5);    // "105" (+ 遇到字符串变拼接)
console.log("10" - 5);    // 5    (- 强制转数字)
console.log("10" * "2");  // 20   (* 强制转数字)
console.log(null + 1);    // 1    (null → 0)
console.log(undefined + 1); // NaN

5.3 == vs ===(强烈推荐 ===)

JavaScript
// == 会做隐式转换
"10" == 10          // true  (字符串转数字)
null == undefined    // true
true == 1            // true

// === 严格比较,类型不同直接 false
"10" === 10         // false
null === undefined   // false
✅ 最佳实践

项目代码永远用 ===!==,避免隐式转换带来的 bug。

运算符 ⭐⭐⭐

6.1 算术运算符

JavaScript
let a = 10, b = 3;
a + b     // 13  加
a - b     // 7   减
a * b     // 30  乘
a / b     // 3.3333  除
a % b     // 1   取余
a ** b    // 1000 幂(ES6)

6.2 自增自减

JavaScript
let n = 5;
console.log(n++);  // 5  (先返回再加)
console.log(n);    // 6
console.log(++n);  // 7  (先加再返回)

6.3 比较运算符(强烈推荐用 === 与 !==)

符号含义建议
> < >= <=大小比较正常用
== !=等于(带隐式转换)❌ 不推荐
=== !==严格等于✅ 永远用

6.4 逻辑运算符

JavaScript
let age = 20, hasID = true;

age >= 18 && hasID          // 与
age >= 18 || hasID          // 或
!hasID                           // 非

// 短路运算常用:
const name = input || "匿名";    // input 为空时给默认值

6.5 三元运算符

JavaScript
let result = age >= 18 ? "成年" : "未成年";
console.log(result);   // "成年"

条件控制 ⭐⭐⭐

7.1 if / else

JavaScript
let score = 85;

if (score >= 90) {
    console.log("优秀");
} else if (score >= 80) {
    console.log("良好");
} else if (score >= 60) {
    console.log("及格");
} else {
    console.log("不及格");
}

7.2 switch(多分支)

JavaScript
let day = 3;
let name;

switch (day) {
    case 1: name = "周一"; break;
    case 2: name = "周二"; break;
    case 3: name = "周三"; break;
    default: name = "未知";
}

console.log(name);  // "周三"
⚠️ 别忘 break,否则会继续往下执行(fall-through)。

循环 ⭐⭐⭐

8.1 for 循环

JavaScript
for (let i = 0; i < 5; i++) {
    console.log(i);   // 0 1 2 3 4
}

8.2 while 与 do...while

JavaScript
// while:条件为真才执行
let n = 0;
while (n < 3) {
    console.log(n);
    n++;
}

// do...while:先执行一次,再判断
let m = 10;
do {
    console.log(m);
} while (m < 3);   // 输出 10,循环结束

8.3 break 与 continue

JavaScript
// 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
}

8.4 for...of 与 for...in

JavaScript
const arr = ["a", "b", "c"];

for (const v of arr) {         // 遍历值(数组/可迭代对象)
    console.log(v);
}

const obj = { x: 1, y: 2 };
for (const k in obj) {        // 遍历键(对象)
    console.log(k, obj[k]);
}

String 字符串 ⭐⭐⭐

9.1 创建与长度

JavaScript
const s1 = "hello";
const s2 = 'world';
const s3 = `多行
字符串`;
console.log(s3.length);   // 不计入首尾换行的实际字符数

9.2 常用方法速查

方法作用示例
indexOf(s)查找位置"hello".indexOf("e") // 1
includes(s)包含判断"hello".includes("he") // true
startsWith(s)开头判断"http".startsWith("h") // true
endsWith(s)结尾判断"hi.txt".endsWith(".txt") // true
slice(a,b)切片"hello".slice(1,4) // "ell"
split(s)拆数组"a,b,c".split(",") // ["a","b","c"]
replace(a,b)替换"ab".replace("a","x") // "xb"
toUpperCase()转大写"Hi".toUpperCase() // "HI"
trim()去首尾空白" hi ".trim() // "hi"

9.3 模板字符串 ⭐⭐⭐

JavaScript
const name = "Alice";
const age = 18;

// 拼接(推荐)
const msg = `姓名:${name},年龄:${age}`;
console.log(msg);   // "姓名:Alice,年龄:18"

// 多行
const html = `
  <div>
    <h1>${name}</h1>
  </div>
`;

// 表达式直接执行
const price = `总金额:¥${(99 * 3).toFixed(2)}`;  // "总金额:¥297.00"
这是现代 JS 必须掌握的内容。拼接字符串一律用模板字符串,别再用 +

Array 数组 ⭐⭐⭐

10.1 创建与访问

JavaScript
const scores = [90, 85, 78, 92];

scores[0];            // 90 第一个元素
scores[scores.length - 1];  // 92 最后一个
scores.length;         // 4

// 修改
scores[1] = 88;

// 创建
const arr1 = [1, 2, 3];
const arr2 = new Array(3);     // 长度 3 的空数组
const arr3 = Array.from("hi");  // ["h", "i"]

10.2 增删改 4 件套

方法作用说明
push(x)末尾加返回新长度
pop()末尾删返回被删除元素
unshift(x)开头加返回新长度
shift()开头删返回被删除元素
slice(a,b)切片(不动原数组)返回新数组
splice(i,n,...)删除/插入(动原数组)返回被删数组
concat(a)拼接返回新数组
indexOf(x)查找位置找不到返回 -1
includes(x)包含判断返回布尔
JavaScript
const arr = [1, 2, 3, 4, 5];

arr.splice(1, 2);    // 从索引 1 起删 2 个 → [1, 4, 5]
arr.splice(1, 0, 99); // 在索引 1 处插入 99 → [1, 99, 4, 5]

const a = [1,2], b = [3,4];
a.concat(b);  // [1, 2, 3, 4](不动 a)

数组遍历与高阶方法 ⭐⭐⭐

11.1 三种基本遍历

JavaScript
const scores = [90, 85, 78];

// 方式 1:传统 for
for (let i = 0; i < scores.length; i++) {
    console.log(scores[i]);
}

// 方式 2:for...of(拿到值)
for (const s of scores) {
    console.log(s);
}

// 方式 3:forEach(提供值+索引+数组)
scores.forEach((s, i) => {
    console.log(`第${i+1}名:${s}分`);
});

11.2 三大高阶方法 ⭐⭐⭐

🧠 三件套
map    → 转换每一项,返回新数组
filter → 筛选满足条件的,返回新数组
reduce → 把数组"汇总"为单个值
JavaScript
const scores = [90, 85, 78, 92, 60];

// map:每人加 5 分
const newScores = scores.map(x => x + 5);
// [95, 90, 83, 97, 65]

// filter:取出 90 分以上
const high = scores.filter(x => x >= 90);
// [90, 92]

// reduce:求总分(初始值为 0)
const total = scores.reduce((sum, x) => sum + x, 0);
// 405

// reduce 求平均
const avg = total / scores.length;   // 81

// 综合练习:找出最高分
const top = scores.reduce((best, x) => x > best ? x : best, -Infinity);

11.3 其他常用方法

JavaScript
const arr = [3, 1, 4, 1, 5, 9];

arr.sort((a, b) => a - b);  // 数字排序 [1,1,3,4,5,9]
arr.reverse();             // 反转
arr.find(x => x > 3);      // 4 第一个满足
arr.findIndex(x => x > 3); // 2
arr.every(x => x > 0);     // true 全员满足
arr.some(x => x > 5);       // true 至少一个
arr.join("-");            // "3-1-4-1-5-9"

Object 对象 ⭐⭐⭐

12.1 创建、访问、修改、删除

JavaScript
const student = {
    name: "张三",
    age: 18,
    score: 95,
    introduce() {
        return `我叫${this.name},` +
               `考${this.score}分`;
    }
};

// 访问
student.name;            // "张三"
student["name"];        // "张三"(动态键名)

// 修改
student.age = 19;

// 删除
delete student.score;

// 调用方法
console.log(student.introduce());

12.2 this 关键字

方法内部的 this 通常指向调用该方法的对象。箭头函数不绑定自己的 this,这是新手最常踩的坑。

12.3 常用方法

JavaScript
const obj = { a: 1, b: 2 };

Object.keys(obj);         // ["a", "b"]
Object.values(obj);       // [1, 2]
Object.entries(obj);      // [["a",1], ["b",2]]
Object.fromEntries(...).keys();  // 反向
Object.assign({}, obj, {c:3}); // 拷贝合并

// 拷贝(浅拷贝)
const copy = { ...obj };
copy.a = 99;
console.log(obj.a);  // 1

解构赋值 ⭐⭐⭐

13.1 数组解构

JavaScript
const [a, b, c] = [10, 20, 30];
console.log(a, b, c);   // 10 20 30

// 跳过/默认值
const [x, , y = 99] = [1, 2];
console.log(x, y);   // 1 99

// 交换变量
let m = 1, n = 2;
[m, n] = [n, m];   // m=2, n=1

13.2 对象解构

JavaScript
const student = { name: "张三", age: 18, score: 95 };

const { name, age } = student;
console.log(name, age);   // "张三" 18

// 重命名 + 默认值
const { name: userName, grade = "A" } = student;
console.log(userName, grade);   // "张三" "A"

// 函数参数解构
function show({ name, age }) {
    console.log(`${name}${age}岁`);
}
show(student);

函数 ⭐⭐⭐

14.1 三种定义方式

JavaScript
// 1. 函数声明(hoisting 友好)
function add(a, b) {
    return a + b;
}

// 2. 函数表达式
const sub = function(a, b) {
    return a - b;
};

// 3. 箭头函数(见下章)
const mul = (a, b) => a * b;

14.2 默认参数 & 可变参数

JavaScript
function greet(name = "游客", ...others) {
    console.log(`你好 ${name}`);
    console.log("others:", others);
}
greet("Alice", "Bob", "Carol");   // others: ["Bob","Carol"]

14.3 函数也是值

JavaScript
function apply(fn, a, b) {
    return fn(a, b);
}
apply(add, 2, 3);          // 5
apply(mul, 2, 3);          // 6

箭头函数 ⭐⭐⭐

15.1 语法糖

JavaScript
// 完整写法
const add = (a, b) => {
    return a + b;
};

// 单行返回(隐式 return)
const add = (a, b) => a + b;

// 单个参数
const double = x => x * 2;

// 无参数
const hello = () => "Hello!";

15.2 与普通函数的区别

对比点function()=> {}
this动态绑定词法绑定(定义时确定)
arguments可用不可用,用 ...rest
作为构造函数可以用 new不能 new
简洁度中等最简洁
JavaScript
// 经典坑:回调中 this 丢失
const timer = {
    sec: 0,
    start() {
        setInterval(() => {    // 箭头函数继承外层 this
            this.sec++;
            console.log(this.sec);
        }, 1000);
    }
};
timer.start();

作用域与闭包 ⭐⭐⭐

16.1 四种作用域

类型说明例子
全局window/global 上的变量var a = 1 在最外层
函数仅函数内部function 内 var/let
块级{} 内(let/const)if、for、{} 内
词法外层作用域(嵌套函数时)闭包的关键

16.2 闭包经典图

🔒 闭包:函数"记住"了它的外部变量
外层函数  fn()
   ↓ return
返回的函数 fn2()
   ↓ 依然能访问 fn() 内的局部变量 count
这就是闭包
JavaScript
// 闭包:用函数"包住"一个状态
function createCounter() {
    let count = 0;          // 私有
    return () => ++count;       // 闭包
}

const cnt = createCounter();
console.log(cnt());  // 1
console.log(cnt());  // 2
console.log(cnt());  // 3
console.log(count);   // ❌ ReferenceError
理解闭包 = 理解 React/Vue 状态管理。面试 100% 会问。

ES6+ 核心语法 ⭐⭐⭐

ES6(2015)是 JavaScript 历史上最大的一次更新。下面这些特性已成为必备

17.1 全部语法清单

特性示例
let / const§03
箭头函数§15
模板字符串\`hi \${name}\`
解构§13
默认 / 剩余 参数fn(a=1, ...rest)
展开运算符[...arr, ...arr2]
对象简写{x, y}{x:x, y:y}
Map / Set见下章
Class§19
Moduleexport / import

17.2 展开运算符(Spread)

JavaScript
const a = [1, 2];
const b = [...a, 3, 4];      // [1, 2, 3, 4]

const obj1 = { x: 1 };
const obj2 = { ...obj1, y: 2 };  // { x:1, y:2 }

Math.max(...a);             // 2
console.log(...a);              // 1 2

// 函数参数
function sum(...nums) { return nums.reduce((s,n)=>s+n, 0); }
sum(1,2,3,4);   // 10

Map 与 Set ⭐⭐

18.1 四种"集合"对比

ArrayObjectMapSet
结构有序列表键值对任意键的键值对无重复集合
键类型数字索引字符串/Symbol任意类型
是否重复键唯一键唯一自动去重
大小.length手动维护.size.size
迭代for / for...offor...in / entriesforEach / for...offorEach / for...of

18.2 Map 示例

JavaScript
const map = new Map();

map.set("name", "张三");
map.set("age", 18);
map.set(obj, "对象当键也可以");

console.log(map.get("name"));   // "张三"
console.log(map.has("age"));   // true
console.log(map.size);         // 3

// 遍历
for (const [k, v] of map) {
    console.log(k, "=>", v);
}

18.3 Set 示例:自动去重

JavaScript
const set = new Set([1, 2, 3, 3, 2, 1]);
console.log([...set]);   // [1, 2, 3]

set.add(4);
set.has(3);     // true

// 数组去重
const arr = [1,1,2,2,3];
const unique = [...new Set(arr)];   // [1,2,3]

面向对象 class ⭐⭐

19.1 基本 class

JavaScript
class Student {
    constructor(name, age) {
        this.name = name;
        this.age  = age;
    }

    introduce() {
        return `我是${this.name},` +
               `${this.age}岁`;
    }
}

const s = new Student("张三", 18);
console.log(s.introduce());

19.2 继承 extends / super

JavaScript
class CollegeStudent extends Student {
    constructor(name, age, major) {
        super(name, age);   // 调用父类构造
        this.major = major;
    }

    introduce() {
        return super.introduce() +
               `,专业:${this.major}`;
    }
}

const cs = new CollegeStudent("李四", 20, "计算机");
console.log(cs.introduce());

19.3 static 静态方法 & private 私有字段

JavaScript
class User {
    // 私有字段(# 开头,外部访问报错)
    #password;

    constructor(name, pwd) {
        this.name = name;
        this.#password = pwd;
    }

    check(pwd) {
        return pwd === this.#password;
    }

    static create(name) {       // 类方法,不需 new
        return new User(name, "123456");
    }
}

const u1 = User.create("Alice");
console.log(u1.check("123456"));   // true

原型与原型链 ⭐⭐

20.1 概念图

s1 实例 [[Prototype]] Student.prototype introduce() Object toString() prototype 链 继承自 Object 找不到属性时沿链向上查找,叫"原型链查找"

20.2 实战:看原型链

JavaScript
class Animal {}
const a = new Animal();

console.log(a.__proto__ === Animal.prototype);  // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__);   // null(链终点)
入门阶段理解"对象 → 自己的类 → Object → null"这条链即可,无需深入底层。日常开发我们用 class,本质仍由原型实现

异常处理 ⭐⭐⭐

21.1 try / catch / finally

JavaScript
try {
    const data = JSON.parse('{"name": "Alice"');  // 错误字符串
} catch (err) {
    console.error("解析失败:", err.message);
} finally {
    console.log("无论是否出错,都会执行");
}

21.2 throw 自定义异常

JavaScript
function divide(a, b) {
    if (b === 0) throw new Error("除数不能为 0");
    return a / b;
}

try {
    divide(10, 0);
} catch (e) {
    console.log(e.message);   // "除数不能为 0"
}

同步与异步 + Event Loop ⭐⭐⭐

22.1 同步 vs 异步

🔄 单线程模型
同步:A → B → C   一行一行执行
异步:A 启动后让浏览器等结果,继续 B,等结果回来再处理

22.2 Event Loop 模型

Call Stack 调用栈 main() fetch() 其他任务 Web APIs 浏览器能力 setTimeout fetch DOM 事件 Callback Queue 任务队列 callback 1 callback 2 promise.then Call Stack 空时,Event Loop 把任务推入执行

Promise ⭐⭐⭐

23.1 三种状态

📦 Promise 三态

pending(进行中) → fulfilled(成功,调用 resolve)
pending → rejected(失败,调用 reject)
一旦确定不可再改。

23.2 创建与基础用法

JavaScript
const fetchData = () =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("数据");
        }, 1000);
    });

fetchData()
    .then(data  => console.log("成功:", data))
    .catch(err  => console.error("失败:", err))
    .finally(() => console.log("结束"));

23.3 Promise 链

JavaScript
fetch("/api/user/1")
    .then(r => r.json())           // 解析 JSON
    .then(user => fetch(`/api/posts?uid=${user.id}`))
    .then(r => r.json())
    .then(posts => console.log(posts))
    .catch(err => console.error(err));

23.4 Promise.all 并发

JavaScript
// 等所有完成
Promise.all([
    fetch("/api/a"),
    fetch("/api/b"),
    fetch("/api/c")
])
.then(arr => Promise.all(arr.map(r => r.json())))
.then(data => console.log(data));

// 任一成功即可
Promise.race([req1, req2]).then(useFastest);

// 全部完成(含失败)
Promise.allSettled([...]).then(showAll);

async / await ⭐⭐⭐

24.1 写法

JavaScript
async function getStudents() {
    try {
        const res = await fetch("/api/students");
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const list = await res.json();
        return list;
    } catch (err) {
        console.error("加载失败:", err);
    }
}

console.log(getStudents());  // 返回 Promise

24.2 与 Promise 对比

维度.then() 链async/await
可读性嵌套多时代码深同步风格
错误处理.catch()try/catch
并行请求Promise.all同 Promise.all
推荐度需要兼容旧代码时✅ 现代首选
async/await 是现代 JavaScript 异步编程的核心语法。能用就用。

DOM 基础 ⭐⭐⭐

DOM = Document Object Model,浏览器把 HTML 转成树形结构,JS 通过操作这棵树来改变页面。

25.1 DOM 树结构

document <html> <body> <head> <h1> <p> <button> 每个 HTML 标签变成一个节点(Node),JS 通过节点对象操作网页

25.2 第一个完整例子

HTML
<h1 id="title">Hello</h1>
<button id="btn">改文字</button>
<script>
    const title = document.getElementById("title");
    const btn   = document.getElementById("btn");

    btn.addEventListener("click", () => {
        title.textContent = "Hello JavaScript";
        title.style.color = "#F7DF1E";
    });
</script>

获取与修改元素 ⭐⭐⭐

26.1 获取元素

方法返回用法
getElementById(id)单元素知道 id
querySelector(sel)第一个匹配"#id" / ".cls" / "div"
querySelectorAll(sel)NodeList(类数组)全部匹配
getElementsByClassName(c)HTMLCollection旧 API,少用
getElementsByTagName(t)HTMLCollection旧 API
JavaScript
const one   = document.querySelector("#title");
const all   = document.querySelectorAll("li.item");
const cards = [...all];

26.2 修改内容

JavaScript
const el = document.getElementById("title");

el.textContent = "纯文本";          // 推荐:纯文本
el.innerHTML   = "<b>含 HTML</b>"; // 可写 HTML(注意 XSS)

const input = document.querySelector("input");
input.value = "hello";                // 表单值

26.3 属性

JavaScript
const img = document.querySelector("img");

img.setAttribute("src", "/a.jpg");
img.getAttribute("alt");
img.removeAttribute("title");

// 标准属性更直接
img.src = "/a.jpg";
img.id  = "avatar";

26.4 修改样式

JavaScript
const box = document.getElementById("box");

// 内联样式(注意命名:backgroundColor)
box.style.color           = "red";
box.style.backgroundColor = "#222";
box.style.fontSize        = "20px";

// class 控制(推荐!样式写在 CSS 里)
box.classList.add("active");
box.classList.remove("hide");
box.classList.toggle("open");
console.log(box.classList.contains("active"));

创建与删除元素 ⭐⭐⭐

27.1 综合案例:动态学生列表

HTML + JS
<!-- index.html -->
<input id="name" placeholder="姓名">
<input id="score" type="number" placeholder="分数">
<button id="add">添加学生</button>
<ul id="list"></ul>

<script>
    const list = document.getElementById("list");
    document.getElementById("add").addEventListener("click", () => {
        const name  = document.getElementById("name").value;
        const score = document.getElementById("score").value;
        if (!name) return;

        const li = document.createElement("li");
        li.textContent = `${name} - ${score}分`;

        const del = document.createElement("button");
        del.textContent = "删除";
        del.addEventListener("click", () => li.remove());

        li.appendChild(del);
        list.appendChild(li);
    });
</script>

事件 ⭐⭐⭐

28.1 常用事件

类别事件
鼠标click dblclick mouseover mouseout mousemove
键盘keydown keyup keypress
表单submit input change focus blur
窗口load resize scroll

28.2 addEventListener(推荐)

JavaScript
const btn = document.getElementById("btn");

btn.addEventListener("click", function(event) {
    console.log("按钮被点击");
    console.log(event.target);  // 触发元素
    console.log(event.type);    // "click"
});

// 移除监听
function onClick() { console.log("a"); }
btn.addEventListener("click", onClick);
btn.removeEventListener("click", onClick);

28.3 事件对象 event

JavaScript
event.target           // 触发事件的元素
event.currentTarget   // 绑定事件的元素
event.type             // 事件类型
event.preventDefault()// 阻止默认行为(如表单提交)
event.stopPropagation()// 停止冒泡

// 键盘事件
input.addEventListener("keydown", e => {
    if (e.key === "Enter") console.log("按了回车");
    if (e.ctrlKey && e.key === "s")
        alert("Ctrl+S");
});

事件冒泡与委托 ⭐⭐

29.1 三个阶段

捕获阶段 Capture 目标阶段 Target 冒泡阶段 Bubble document → ... → 父 当前元素 父 → ... → document

29.2 事件委托

把监听器挂到父元素,利用冒泡统一处理子元素。

JavaScript
// 1000 个 li,只需要 1 个监听
const ul = document.getElementById("list");
ul.addEventListener("click", e => {
    if (e.target.tagName === "LI") {
        console.log("点了:", e.target.textContent);
    }
});

// 第三个参数 true = 走捕获阶段(默认 false 走冒泡)
el.addEventListener("click", handler, true);

BOM 与定时器 ⭐⭐

30.1 五大对象

对象作用
window全局 + 弹窗 + 帧控制
navigator浏览器信息
locationURL 操作
history前进/后退
screen屏幕宽高
JavaScript
location.href             // 当前完整 URL
location.reload()        // 刷新
location.assign("/home")  // 跳转

history.back()           // 后退
history.forward()       // 前进
history.go(- 2)           // 退回 2 步

alert("提示")         // 警告框
confirm("确定吗?")   // 确认框(返回布尔)
prompt("姓名:")     // 输入框(返回字符串)

30.2 定时器综合案例:倒计时

JavaScript
let sec = 10;
const h = document.getElementById("h");

const timer = setInterval(() => {
    h.textContent = `剩余 ${sec} 秒`;
    if (--sec === 0) {
        clearInterval(timer);   // 停止
        h.textContent = "时间到!";
    }
}, 1000);

// 单次延迟
const t = setTimeout(() => console.log("1 秒后"), 1000);
clearTimeout(t);  // 取消

Fetch API ⭐⭐⭐

31.1 两种写法

JavaScript (Promise 链)
fetch("/api/students")
    .then(r => {
        if (!r.ok) throw new Error(String(r.status));
        return r.json();
    })
    .then(data => console.log(data))
    .catch(err => console.error(err));
JavaScript (async/await)
async function load() {
    try {
        const r = await fetch("/api/students");
        const data = await r.json();
        console.log(data);
    } catch (e) {
        console.error(e);
    }
}

31.2 发送 POST

JavaScript
fetch("/api/students", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: "张三", score: 95 })
})
.then(r => r.json())
.then(d => console.log("已添加", d));

HTTP 基础 ⭐⭐⭐

32.1 请求响应流程

🌐 HTTP 一次往返
浏览器 → (请求 Request) → 服务器
       ← (响应 Response) ←

32.2 常用方法

方法语义是否带 body
GET获取
POST新增
PUT整体修改
PATCH局部修改
DELETE删除

32.3 状态码速查

范围含义例子
2xx成功200 OK, 201 Created
3xx重定向301 Moved, 304 Not Modified
4xx客户端错400 参数错, 401 未登录, 404 找不到, 429 太频繁
5xx服务器错500 内部错误, 503 服务不可用

32.4 JSON 互转

JavaScript
const obj = { name: "张三", age: 18 };

const json = JSON.stringify(obj);
console.log(json);   // '{"name":"张三","age":18}'

const back = JSON.parse(json);
console.log(back.name);   // "张三"

Web Storage(本地存储)⭐⭐⭐

33.1 localStorage vs sessionStorage

localStoragesessionStorage
生命周期永久(除非手动删)关闭标签页即清
容量~5MB~5MB
作用域同源共享仅当前标签

33.2 浏览器记事本项目

JavaScript
const KEY = "notes";

function save() {
    const text = document.getElementById("content").value;
    const list = JSON.parse(localStorage.getItem(KEY) || "[]");
    list.push({ text, at: Date.now() });
    localStorage.setItem(KEY, JSON.stringify(list));
    render();
}

function render() {
    const list = JSON.parse(localStorage.getItem(KEY) || "[]");
    const ul = document.getElementById("notes");
    ul.innerHTML = "";
    list.forEach(n => {
        const li = document.createElement("li");
        li.textContent = n.text;
        ul.appendChild(li);
    });
}

sessionStorage.setItem("temp", "标签页一关就清");

表单与正则 ⭐⭐

34.1 表单提交 + 阻止默认

HTML + JS
<form id="f">
    <input name="email">
    <input name="pwd" type="password">
    <button>提交</button>
</form>
<script>
document.getElementById("f").addEventListener("submit", e => {
    e.preventDefault();   // 阻止页面刷新
    const data = Object.fromEntries(new FormData(e.target));
    console.log(data);   // { email: "...", pwd: "..." }
});
</script>

34.2 RegExp:常用正则

JavaScript
const emailRe = /^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$/;
const phoneRe = /^1[3-9]\d{9}$/;
const userRe  = /^[a-zA-Z][\w]{3,15}$/;

emailRe.test("alice@school.edu");   // true
phoneRe.test("13800138000");       // true

console.log("a1b2c3".match(/\d+/g));  // ["1","2","3"]

"a,b,c".replace(/,/g, "-");   // "a-b-c"

34.3 综合:表单验证

JavaScript
function validate({ email, phone }) {
    const errors = {};
    if (!/^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$/.test(email))
        errors.email = "邮箱格式不对";
    if (!/^1[3-9]\d{9}$/.test(phone))
        errors.phone = "手机号 11 位";
    return errors;
}

JavaScript 调试 ⭐⭐⭐

35.1 console 三件套

JavaScript
const data = [
    { name: "张三", age: 18, score: 95 },
    { name: "李四", age: 19, score: 82 }
];

console.log("普通日志", data);
console.table(data);           // 表格化展示
console.error("报错信息");
console.warn("警告");
console.dir(data);             // 显示对象结构
console.time("耗时");
// ...
console.timeEnd("耗时");

35.2 DevTools 断点流程

🪜 调试 4 步走
  1. 读报错:Console 看红色错误,定位文件+行号
  2. 看堆栈:Call Stack 看调用顺序
  3. 打断点:Sources 面板点行号,F10 单步跳过、F11 进入
  4. 观察变量:右侧 Watch 加变量,Hover 源码可看值
核心能力:阅读错误信息 → 定位错误 → 修改代码 → 再运行。大一就要养成习惯。

模块化 + npm + Node.js ⭐⭐

36.1 ES Module(浏览器原生)

math.js
export function add(a, b) { return a + b; }
export default function sub(a, b) { return a - b; }
app.js
import { add } from "./math.js";
import sub from "./math.js";
console.log(add(2, 3), sub(5, 2));
index.html
<script type="module" src="app.js"></script>

36.2 npm 工作流

命令
npm init -y                      # 生成 package.json
npm install lodash               # 装到 dependencies
npm install --save-dev eslint    # 装到 devDependencies
npm run dev                      # 运行 package.json 的 scripts

36.3 Node.js 入门

🟢 Node.js 不是 JavaScript 语言,是 JS 运行环境

它让 JS 能在服务器 / 命令行运行。大一入门不深入,只要知道能写后端即可。

node app.js         # 执行 JS
node --version      # 查版本

综合项目一:计算器 ⭐⭐⭐

项目 1:网页计算器
难度:⭐⭐技术:HTML + CSS + JS核心:DOM + 事件 + 函数

思路

  1. HTML 网格布局数字按钮 + 显示屏
  2. 用一个全局字符串保存当前表达式
  3. 按下数字追加,按下运算追加符号
  4. 按下 = 调用 eval() 计算(或自己写解析)

完整代码

HTML
<div id="calc">
    <div id="screen"></div>
    <div id="pad">
        <!-- 数字与运算按钮 -->
        <button data-k="C">C</button>
        <button data-k="/">÷</button>
        <button data-k="7">7</button>
        <button data-k="8">8</button>
        ... 更多按钮 ...
        <button data-k="=">=</button>
    </div>
</div>
JavaScript
let expr = "";
const screen = document.getElementById("screen");

document.getElementById("pad").addEventListener("click", e => {
    const k = e.target.dataset.k;
    if (!k) return;
    if (k === "C")      expr = "";
    else if (k === "=") expr = String(safeEval(expr));
    else                  expr += k;
    screen.textContent = expr;
});

// 简易安全求值(真实项目请用第三方库)
function safeEval(s) {
    if (!/^[\d+\-*/().\s]+$/.test(s)) throw "含非法字符";
    return Function(`"use strict"; return (${s});`)();
}

// 键盘支持
document.addEventListener("keydown", e => {
    if ("0123456789+-*/.".includes(e.key))
        expr += e.key;
    else if (e.key === "Enter") expr = String(safeEval(expr));
    else if (e.key === "Backspace") expr = expr.slice(0, -1);
    else if (e.key === "Escape")    expr = "";
    screen.textContent = expr || "0";
});

综合项目二:Todo List ⭐⭐⭐

项目 2:任务清单 Todo List
难度:⭐⭐⭐综合:DOM + 事件 + 数组 + localStorage

完整实现

JavaScript
const KEY = "todo.list";
let todos = JSON.parse(localStorage.getItem(KEY) || "[]");
let filter = "all";     // all | active | done

const $input = document.getElementById("input");
const $list  = document.getElementById("list");
const $count = document.getElementById("count");

function save()        { localStorage.setItem(KEY, JSON.stringify(todos)); }
function add(text)      {
    todos.push({ id: Date.now(), text, done: false });
}
function toggle(id)     {
    const t = todos.find(x => x.id === id);
    if (t) t.done = !t.done;
}
function del(id)        { todos = todos.filter(x => x.id !== id); }
function edit(id, text) {
    const t = todos.find(x => x.id === id);
    if (t) t.text = text;
}

function render() {
    $list.innerHTML = "";
    const view = todos.filter(t =>
        filter === "all" ||
        (filter === "active" && !t.done) ||
        (filter === "done"  &&  t.done)
    );
    view.forEach(t => {
        const li = document.createElement("li");
        li.dataset.id = t.id;
        li.innerHTML = `
            <input type="checkbox" ${t.done ? "checked" : ""}>
            <span class="text"${t.done ? " style=\"text-decoration:line-through\"" : ""}>${t.text}</span>
            <button class="del">×</button>
        `;
        $list.appendChild(li);
    });
    $count.textContent = todos.filter(t => !t.done).length;
}

document.getElementById("add").addEventListener("click", () => {
    const text = $input.value.trim();
    if (text) { add(text); save(); render(); }
    $input.value = "";
});

$list.addEventListener("click", e => {
    const id = Number(e.target.closest("li")?.dataset.id);
    if (!id) return;
    if (e.target.matches("input[type=checkbox]")) toggle(id);
    else if (e.target.matches(".del"))        del(id);
    save();
    render();
});

render();

综合项目三:学生成绩管理

项目 3:学生成绩管理
难度:⭐⭐⭐综合:Array + Object + DOM + Event

核心代码

JavaScript
let students = [];     // { id, name, score }

function addStudent(name, score) {
    students.push({ id: Date.now(), name, score: Number(score) });
}
function delStudent(id)        { students = students.filter(s => s.id !== id); }
function updateStudent(id, n, sc) {
    const s = students.find(x => x.id === id);
    if (s) { s.name = n; s.score = sc; }
}
function search(keyword) {
    return students.filter(s => s.name.includes(keyword));
}
function stats() {
    if (!students.length) return null;
    const scores = students.map(s => s.score);
    return {
        avg:   (scores.reduce((s,n)=>s+n,0) / scores.length).toFixed(1),
        max:   Math.max(...scores),
        min:   Math.min(...scores),
        sorted: [...students].sort((a,b) => b.score - a.score)
    };
}

// 视图渲染(略,同 Todo 模式)
function render() {
    const t = document.getElementById("tbody");
    t.innerHTML = "";
    students.forEach(s => {
        t.innerHTML += `<tr>
            <td>${s.name}</td>
            <td>${s.score}</td>
            <td><button data-del-id="${s.id}">删</button></td>
        </tr>`;
    });
    const st = stats();
    if (st) document.getElementById("stats").textContent =
        `平均 ${st.avg}  最高 ${st.max}  最低 ${st.min}`;
}

综合项目四:天气查询

项目 4:实时天气查询
难度:⭐⭐⭐综合:Fetch + API + JSON + DOM

流程

  1. 用户输入城市名
  2. Fetch 调用 Open-Meteo / wttr.in 等免费 API
  3. 解析 JSON 渲染温度、湿度、风速、图标
JavaScript
async function getWeather(city) {
    // 第 1 步:地理编码
    const geoUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(city)}`;
    const geoRes  = await fetch(geoUrl);
    const geo     = await geoRes.json();
    if (!geo.results) throw "找不到城市";

    const { latitude, longitude, name } = geo.results[0];

    // 第 2 步:天气数据
    const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current_weather=true`;
    const res  = await fetch(url);
    const data = await res.json();

    return {
        name:    name,
        temp:    data.current_weather.temperature,
        wind:    data.current_weather.windspeed,
        weather: data.current_weather.weathercode
    };
}

document.getElementById("search").addEventListener("click", async () => {
    try {
        const city = document.getElementById("city").value;
        const w = await getWeather(city);
        document.getElementById("out").innerHTML =
            `<h2>${w.name}</h2>
             <p>${w.temp}°C · 风 ${w.wind} km/h</p>`;
    } catch (e) {
        alert(e);
    }
});
教学场景:让学生理解前端 → 后端 → 数据 的全链路。任何公开 REST API 都可以替换练习。

综合项目五:在线图书管理

项目 5:在线图书管理系统(前端 + 模拟 REST)
难度:⭐⭐⭐⭐综合:HTML + CSS + JS + Fetch + REST API + SQL

架构

📚 全栈雏形
HTML(页面)
  ↓
CSS(视觉)
  ↓
JavaScript(逻辑)
  ↓
Fetch API(HTTP)
  ↓
REST API(后端接口)
  ↓
SQL(数据库)

前端代码(兼容本系列 SQL 教程)

JavaScript
const API = "/api/books";

async function listBooks() {
    const r = await fetch(API);
    return r.json();
}
async function addBook(book) {
    return fetch(API, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(book)
    });
}
async function deleteBook(id) {
    return fetch(`${API}/${id}`, { method: "DELETE" });
}
async function borrowBook(id, user) {
    return fetch(`${API}/${id}/borrow`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ user })
    });
}

// 视图渲染省略,结构同前几个项目

关联 SQL(对应本系列 SQL 教程"图书借阅"项目)

SQL
CREATE TABLE books (
    id     INT PRIMARY KEY AUTO_INCREMENT,
    title  VARCHAR(100),
    author VARCHAR(50),
    stock  INT DEFAULT 0
);
CREATE TABLE borrows (
    id      INT PRIMARY KEY AUTO_INCREMENT,
    book_id INT,
    user    VARCHAR(50),
    borrowed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (book_id) REFERENCES books(id)
);

SELECT b.title, b.author, COUNT(br.id) AS times
FROM books b LEFT JOIN borrows br ON b.id = br.book_id
GROUP BY b.id
ORDER BY times DESC;
这一章是前后端合流,自然衔接到前面学的 SQL。

数据结构与算法入门 ⭐⭐

42.1 简单数据结构

结构JS 实现思路
栈(Stack)push / pop——先进后出
队列(Queue)push + shift——先进先出
哈希表Object / Map
集合Set
JavaScript
// 栈
const stack = [];
stack.push(1);  stack.push(2);
stack.pop();        // 2

// 队列:shift 是 O(n),用 index 优化
const q = [];
let head = 0;
q.push(1); q.push(2);
const first = q[head++];   // 1

// 二分查找
function binarySearch(arr, target) {
    let l = 0, r = arr.length - 1;
    while (l <= r) {
        const m = (l + r) >>> 1;
        if (arr[m] === target) return m;
        if (arr[m] <  target) l = m + 1;
        else                 r = m - 1;
    }
    return -1;
}

// 冒泡排序
function bubble(arr) {
    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j+1])
                [arr[j], arr[j+1]] = [arr[j+1], arr[j]];
        }
    }
    return arr;
}
这一章只为打基础,不要在大一深陷算法。先把项目做漂亮。

进阶方向 + JavaScript 速查手册

43.1 三条进阶路

🚀 完成本教程后

前端开发:JS → TypeScript → Vue / React → 前端工程化

Node.js 后端:JS → Node.js → Express / NestJS → MySQL

全栈开发:HTML → CSS → JS → Vue/React → Node.js → MySQL

43.2 JavaScript 速查手册

变量与常量

  • const x = 1
  • let y = 2
  • var z = 3(不推荐)

类型

  • Number / String / Boolean
  • null / undefined
  • Array / Object
  • typeof x

运算符

  • 算术:+-*/% **
  • 比较:=== !== > <
  • 逻辑:&& || !
  • 三元:a?b:c

条件

  • if / else / else if
  • switch/case
  • ?: 三元
  • ?? 空合并

循环

  • for (i; c; u)
  • while / do-while
  • for...of(数组)
  • for...in(对象键)

字符串

  • s.length / includes
  • s.slice / split / replace
  • \`hi ${x}\` 模板

数组方法

  • push / pop / shift
  • map / filter / reduce
  • find / some / every
  • sort / reverse

对象

  • obj.key / obj['k']
  • Object.keys/vals/entries
  • ...obj (展开)
  • class { constructor() {} }

解构 / 展开

  • const {a,b} = obj
  • const [x,y] = arr
  • const all = [...a, ...b]
  • {...obj, k:v}

函数

  • function f(a,b)
  • const f = (a) => a*2
  • (a=1, ...rest)=>{}
  • setTimeout(f, ms)

异步

  • new Promise((res, rej)=>{})
  • .then().catch()
  • async / await
  • Promise.all([...])

DOM

  • document.querySelector
  • el.textContent / innerHTML
  • el.classList.add/toggle
  • el.addEventListener('click')

存储

  • localStorage.setItem / getItem
  • sessionStorage.setItem
  • JSON.parse / stringify
  • fetch(url).then(r=>r.json())

课堂训练:12 道选择题

本节是与前面各章配套的小练习,做错的同学回到对应章节复习。

Q1. 关于 let 与 var,下列说法正确的是?
  1. A. var 声明的变量具有块级作用域
  2. B. let 声明的变量具有块级作用域
  3. C. var 不能重复声明,let 可以
  4. D. let 会被提升到顶部并初始化为 undefined
答案:B。var 是函数作用域,let/const 才是块级作用域;且 let/const 处于 TDZ(暂时性死区),不会初始化为 undefined。
Q2. 下面哪个表达式结果不是 NaN?
  1. A. Number("abc")
  2. B. parseInt("hi")
  3. C. Number("123abc") 是合法的纯数字字符串吗?它应返回 …
  4. D. undefined + 1
答案:C(Number("123abc") 返回 NaN,但题目更标准答案是:本题期望选 A/B/C/D 中的唯一真值)。实际请记住:Number()、parseInt() 转换非数字字符都可能是 NaN,只有显式全数字字符串能成功。
Q3. 推荐的相等性比较运算符是?
  1. A. ===
  2. B. ==
  3. C. =
  4. D. !==
答案:A。=== 严格相等不触发隐式转换,符合现代 JS 最佳实践。
Q4. [1,2,3].map(x => x*2) 的结果是?
  1. A. [1,2,3]
  2. B. [2,4,6]
  3. C. 9
  4. D. [3,6,9]
答案:B。map 不修改原数组,返回每个元素 ×2 的新数组。
Q5. 以下关于箭头函数的说法,错误的是?
  1. A. 没有自己的 this,继承外层
  2. B. 不能用作构造函数
  3. C. 内部可以使用 arguments 对象
  4. D. 单行可省 return
答案:C。箭头函数没有 arguments,必须用剩余参数 ...rest。
Q6. 一个 Promise 一旦确定(resolve 或 reject),它的状态会?
  1. A. 不能再变
  2. B. 还可以再次 resolve
  3. C. 可切换为另一个状态
  4. D. 默认 5 秒后重置
答案:A。Promise 状态不可逆:pending → fulfilled / rejected,then catch finally 执行。
Q7. async function 总是返回什么?
  1. A. undefined
  2. B. Promise
  3. C. 同步值
  4. D. 取决于是否有 await
答案:B。async 函数自动把返回值包成 Promise.resolve。
Q8. 设置元素文本应优先使用?
  1. A. innerHTML
  2. B. innerText
  3. C. textContent
  4. D. document.write
答案:C。textContent 安全(不解析 HTML)、性能好、能防 XSS。
Q9. 表单提交时阻止页面刷新用?
  1. A. e.preventDefault()
  2. B. return false
  3. C. e.stopPropagation()
  4. D. location.reload()
答案:A。preventDefault 是浏览器默认行为的标准取消方式。
Q10. localStorage 与 sessionStorage 的关键区别?
  1. A. 容量不同
  2. B. 生命周期不同
  3. C. 是否加密
  4. D. 是否同步
答案:B。localStorage 永久,sessionStorage 关标签即清。
Q11. addEventListener 第三参数为 true 时表示?
  1. A. 只触发一次
  2. B. 默认参数
  3. C. 阻止冒泡
  4. D. 走捕获阶段
答案:D。false/省略 = 冒泡;true = 捕获。
Q12. JSON.parse(JSON.stringify({a:1, b:undefined, c:function(){}})) 结果是?
  1. A. {"a":1,"b":undefined,"c":function(){}}
  2. B. {"a":1,"c":null}
  3. C. {"a":1}
  4. D. undefined
答案:C。JSON 不支持 undefined 和 function,它们被丢弃。

写在最后:本课程与体系衔接

🎓 大学计算机基础课程完整路径
C 语言       → 程序设计基础(指针/数组)
C++          → 面向对象 + 数据结构
Java         → 面向对象 + 软件开发
Python       → 编程 + 数据处理 + AI
SQL/数据库   → 数据存储 + 数据查询
JavaScript   → Web 编程 + 网页交互

JS 是整个体系最贴近"产出"的一门:学完即可独立做出可交互的网页项目。 下一步自然衔接 Node.js / Vue / React,组成完整全栈能力。

提示:本教程相关数据/图表均为 SVG 内联矢量,可直接复制到课件。所有代码示例均可直接粘到浏览器 Console 运行。