HTML/CSS/JS 学习笔记

一份覆盖完整知识点的 HTML + CSS + JavaScript 前端学习笔记。重点讲"为什么这么设计"(费曼式 📌 讲解),配可直接运行的代码示例。

怎么用这份笔记

  1. 学习/复习 → 顺序看正文,重点看 📌 类比和"为什么"
  2. 查语法 → 翻对应章节,代码示例可直接复制运行
  3. 查标签/属性 → 翻 附录 A/B/C 速查表
  4. 调试问题 → 查 附录 D 调试技巧

1 · HTML 概述与文档结构

1.1 HTML 是什么

HTML (HyperText Markup Language) 是描述网页结构的标记语言。它不是编程语言,而是用标签定义内容的语义结构。

TIP

📌 打个比方:HTML 像建房子的图纸——它定义"这里是客厅、那里是卧室、这面墙是承重墙",但不关心墙刷什么颜色(那是 CSS 的活),也不关心门怎么开关(那是 JS 的活)。三者分工:HTML 管结构、CSS 管样式、JS 管行为。

1.2 文档基本结构

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>页面标题</title>
</head>
<body>
  <h1>Hello World</h1>
</body>
</html>
元素 作用
<!DOCTYPE html> 声明 HTML5 文档类型
<html lang="..."> 根元素,lang 声明语言
<head> 元数据区(不显示在页面上)
<meta charset="UTF-8"> 字符编码声明
<meta name="viewport"> 移动端视口设置
<title> 浏览器标签页标题
<body> 可见内容区
TIP

📌 为什么 <!DOCTYPE html> 这么短:HTML4 的 DOCTYPE 要写一长串 DTD URL(因为 SGML 要求引用文档类型定义)。HTML5 不基于 SGML,DOCTYPE 只是告诉浏览器"用标准模式渲染",不需要 DTD 引用。简短声明 = 标准模式,省略 = 怪异模式(兼容老浏览器)。


2 · 语义化标签

2.1 为什么需要语义化

HTML5 引入了一批语义化标签,替代通用的 <div>:

标签 语义 常见用途
<header> 页头/区头 站点顶部、文章标题区
<nav> 导航 菜单、面包屑
<main> 主要内容 页面核心区域(每页唯一)
<article> 独立内容 文章、帖子、评论
<section> 内容分区 按主题划分的区域
<aside> 旁侧内容 侧边栏、广告、相关链接
<footer> 页脚/区脚 版权信息、联系方式
<figure> 图文组合 图片+说明文字
<time> 时间 机器可读的日期
TIP

📌 语义化标签的好处:① 搜索引擎理解内容结构(SEO);② 屏幕阅读器为视障用户导航;③ 代码可读性高。<div class="header"> 对机器无意义,<header> 对机器有明确语义。

2.2 常用文本标签

<h1>~<h6>      标题(h1 每页建议唯一)
<p>            段落
<strong>       强调(语义=重要,默认加粗)
<em>           强调(语义=语气重,默认斜体)
<mark>         标记(默认黄色背景)
<code>         行内代码
<pre>          预格式化文本(保留空格换行)
<blockquote>   块级引用
<a href="..."> 超链接
<ul>/<ol>/<li> 无序/有序列表
TIP

📌 <strong> vs <b>:<b> 只是视觉加粗,无语义;<strong> 表示"内容重要",屏幕阅读器会加重语气朗读。同理 <em> vs <i>。永远优先用语义标签,样式交给 CSS。


3 · 表单与输入

3.1 表单基础

<form action="/submit" method="POST">
  <label for="name">姓名:</label>
  <input type="text" id="name" name="name" required>

  <label for="email">邮箱:</label>
  <input type="email" id="email" name="email" placeholder="example@mail.com">

  <select name="city">
    <option value="bj">北京</option>
    <option value="sh">上海</option>
  </select>

  <textarea name="msg" rows="4" placeholder="留言内容"></textarea>

  <button type="submit">提交</button>
</form>

3.2 input 类型

type 说明 HTML5新增
text 单行文本
password 密码(掩码显示)
email 邮箱(带验证) ✓
number 数字(带步进器) ✓
date / time / datetime-local 日期/时间选择器 ✓
color 颜色选择器 ✓
range 滑块 ✓
file 文件上传
checkbox / radio 复选/单选
hidden 隐藏字段
TIP

📌 为什么 HTML5 新增这么多 input type:以前所有输入都是 text,验证全靠 JS。HTML5 让浏览器原生支持类型验证和专用 UI(如日期选择器),减少 JS 代码量,提升一致性和可访问性。


4 · 多媒体与嵌入

4.1 图片

<!-- 响应式图片 -->
<img src="photo.jpg" alt="描述文字" width="800" height="600">

<!-- srcset 适配不同分辨率 -->
<img src="small.jpg"
     srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
     sizes="(max-width: 600px) 480px, 800px"
     alt="响应式图片">
TIP

📌 为什么用 srcset:手机屏幕小但像素密度高(Retina),如果只给一张大图,手机下载浪费流量;只给小图,Retina 屏模糊。srcset 让浏览器根据屏幕尺寸和像素密度自动选最合适的图片。

4.2 音频与视频

<audio controls>
  <source src="audio.mp3" type="audio/mpeg">
  您的浏览器不支持音频。
</audio>

<video controls width="640" height="360" poster="cover.jpg">
  <source src="video.mp4" type="video/mp4">
  <track src="subtitles.vtt" kind="subtitles" srclang="zh" label="中文字幕">
  您的浏览器不支持视频。
</video>

4.3 iframe 嵌入

<iframe src="https://example.com" width="600" height="400"
        sandbox="allow-scripts allow-same-origin" loading="lazy">
</iframe>
TIP

📌 sandbox 属性:默认禁止 iframe 内的所有脚本执行、表单提交、弹窗等。通过白名单逐个放行需要的权限。这是防御 iframe 嵌入内容被恶意利用的安全机制。


5 · CSS 基础与选择器

5.1 CSS 是什么

CSS (Cascading Style Sheets) 控制网页的视觉表现——颜色、字体、布局、动画。核心机制:选择器 + 属性声明。

5.2 引入方式

<!-- 1. 外部样式表(推荐) -->
<link rel="stylesheet" href="style.css">

<!-- 2. 内部样式 -->
<style>
  p { color: #333; }
</style>

<!-- 3. 行内样式(不推荐,维护性差) -->
<p style="color: red;">文字</p>

5.3 选择器

选择器 语法 示例 优先级
标签 tag p {} 0,0,1
类 .class .btn {} 0,1,0
ID #id #header {} 1,0,0
属性 [attr] [type="text"] {} 0,1,0
后代 A B div p {} 合计
子代 A > B div > p {} 合计
相邻 A + B h1 + p {} 合计
通用 * * {} 0,0,0
伪类 :hover a:hover {} 0,1,0
伪元素 ::before p::before {} 0,1,0
TIP

📌 CSS 优先级计算:把选择器拆成 (ID数, 类数, 标签数) 三元组比较。#nav .item a = (1,1,1),.sidebar .item = (0,2,0)。从左到右比,左边大的胜出。行内样式 style="" = (1,0,0,0) 四元组,永远高于选择器。!important 是最后手段,会覆盖一切。

5.4 盒模型

┌─────────────────────────────────┐ │ margin (外边距) │ │ ┌───────────────────────────┐ │ │ │ border (边框) │ │ │ │ ┌─────────────────────┐ │ │ │ │ │ padding (内边距) │ │ │ │ │ │ ┌───────────────┐ │ │ │ │ │ │ │ content │ │ │ │ │ │ │ └───────────────┘ │ │ │ │ │ └─────────────────────┘ │ │ │ └───────────────────────────┘ │ └─────────────────────────────────┘
/* 标准盒模型:width = content 宽度 */
.box { box-sizing: content-box; width: 200px; padding: 20px; }
/* 实际占用 = 200 + 20*2 = 240px */

/* 边框盒模型:width = content + padding + border */
.box { box-sizing: border-box; width: 200px; padding: 20px; }
/* 实际占用 = 200px(padding 包含在内) */
TIP

📌 为什么推荐 border-box:标准盒模型下,加 padding 会让元素变宽,布局容易溢出。border-box 让 width 就是最终宽度,所见即所得。全局设置 * { box-sizing: border-box; } 是前端标配。


6 · Flexbox 布局

6.1 Flex 基本概念

.container {
  display: flex;
  flex-direction: row;        /* row | column | row-reverse | column-reverse */
  justify-content: center;    /* 主轴对齐 */
  align-items: center;        /* 交叉轴对齐 */
  flex-wrap: wrap;            /* 换行 */
  gap: 16px;                  /* 间距 */
}

6.2 Flex 属性

属性 作用 值
flex-direction 主轴方向 row column row-reverse column-reverse
justify-content 主轴对齐 flex-start center flex-end space-between space-around space-evenly
align-items 交叉轴对齐 stretch flex-start center flex-end baseline
flex-wrap 换行 nowrap wrap wrap-reverse
flex-grow 放大比例 数字(0=不放大)
flex-shrink 缩小比例 数字(1=可缩小)
flex-basis 初始大小 auto 或具体值
order 排列顺序 数字(默认0)
TIP

📌 Flexbox 解决了什么:在 Flexbox 之前,垂直居中是 CSS 最头疼的问题——需要 position: absolute + 负 margin 或 line-height hack。Flexbox 只需 display: flex; align-items: center; justify-content: center; 三行搞定。它是一维布局的最佳方案。

/* 经典:水平+垂直居中 */
.center {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

7 · Grid 布局

7.1 Grid 基本概念

.grid {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;    /* 三列,中间列占两份 */
  grid-template-rows: auto 1fr auto;      /* 三行 */
  gap: 20px;
}

/* 响应式:自动填充,每列最小 200px */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}

7.2 Grid vs Flexbox

Flexbox Grid
维度 一维(行或列) 二维(行+列)
适合 导航栏、工具栏、卡片行 整体页面布局、仪表盘
对齐 主轴+交叉轴 行+列精确控制
排序 order grid-area 任意位置
TIP

📌 什么时候用 Grid 什么时候用 Flex:需要同时控制行和列 → Grid。只需要控制一个方向 → Flex。实际项目中外层用 Grid 做页面骨架,内层用 Flex 做组件布局,两者配合使用。


8 · 响应式设计

8.1 媒体查询

/* 默认样式(移动端优先) */
.container { padding: 12px; }

/* 平板 */
@media (min-width: 768px) {
  .container { padding: 24px; }
}

/* 桌面 */
@media (min-width: 1024px) {
  .container { padding: 40px; max-width: 1200px; margin: 0 auto; }
}

8.2 响应式单位

单位 说明
px 绝对像素
% 相对父元素
em 相对自身/父元素字体大小
rem 相对根元素(html)字体大小
vw / vh 视口宽度/高度的 1%
vmin / vmax 视口较小/较大边的 1%
clamp(min, val, max) 在 min~max 范围内取 val
/* 流式排版:最小 16px,理想 5vw,最大 32px */
h1 { font-size: clamp(1rem, 5vw, 2rem); }
TIP

📌 移动端优先 vs 桌面端优先:移动端优先(min-width 媒体查询)是现代做法——先写最小屏幕的样式,逐步增强。因为移动端设备性能弱,不需要的样式不应该加载。桌面端优先则是先写完整样式再做降级,容易产生冗余。


9 · 动画与过渡

9.1 过渡 (Transition)

.btn {
  background: #3498db;
  transition: background 0.3s ease, transform 0.2s ease;
}
.btn:hover {
  background: #2980b9;
  transform: scale(1.05);
}

9.2 动画 (Animation)

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

.element {
  animation: fadeIn 0.5s ease forwards;
}

9.3 性能注意

属性 是否触发重排 性能
transform 否(GPU合成层) 最好
opacity 否(GPU合成层) 最好
color / background 仅重绘 好
width / height / margin 重排 差
top / left 重排 差
TIP

📌 为什么动画用 transform 而不是 top/left:改变 top/left 会触发浏览器重新计算布局(重排 Reflow),影响所有元素。transform 只在合成阶段处理(GPU 层),不触发布局计算。60fps 动画的秘诀:只用 transform 和 opacity。


10 · JavaScript 概述与基础语法

10.1 JS 是什么

JavaScript 是浏览器的脚本语言,控制网页行为。现在也通过 Node.js 运行在服务端。

TIP

📌 JS 和 Java 没关系:1995 年 Netscape 的 Brendan Eich 用 10 天设计出 JS,最初叫 Mocha → LiveScript → JavaScript。改名 JavaScript 纯粹是营销策略——蹭当时 Java 的热度。两者语法风格、类型系统、设计哲学完全不同。

10.2 引入方式

<!-- 外部脚本(推荐) -->
<script src="app.js" defer></script>

<!-- 内部脚本 -->
<script>
  console.log("Hello");
</script>
TIP

📌 defer vs async:默认 <script> 会阻塞 HTML 解析。defer:异步下载,等 HTML 解析完后按顺序执行。async:异步下载,下载完立即执行(可能中断 HTML 解析)。99% 的情况用 defer,它保证脚本能访问完整 DOM 且按顺序执行。

10.3 变量声明

let x = 10;       // 可变,块级作用域
const y = 20;     // 不可变,块级作用域
var z = 30;       // 可变,函数作用域(已不推荐)
TIP

📌 为什么不用 var:var 有两个坑——① 函数作用域(而非块级),变量泄漏到函数外;② 变量提升(hoisting),在声明前访问不报错而是 undefined。let/const 是块级作用域且存在暂时性死区(TDZ),声明前访问直接报错,更安全。


11 · 数据类型与类型转换

11.1 基本类型 vs 引用类型

类型 类别 说明
String 基本类型 字符串
Number 基本类型 数字(整数+浮点,统一 64 位浮点)
Boolean 基本类型 true / false
undefined 基本类型 未定义
null 基本类型 空值(typeof 返回 "object",历史 bug)
Symbol 基本类型 唯一标识符(ES6)
BigInt 基本类型 任意精度整数(ES2020)
Object 引用类型 对象、数组、函数、Map、Set 等

11.2 类型判断

typeof "hello"        // "string"
typeof 42             // "number"
typeof true           // "boolean"
typeof undefined      // "undefined"
typeof null           // "object"  ← 历史遗留 bug
typeof {}             // "object"
typeof []             // "object"  ← 数组也是 object
typeof function(){}   // "function"

// 精确判断
Array.isArray([1,2])  // true
null === null         // true
Object.prototype.toString.call([])  // "[object Array]"

11.3 类型转换

// 隐式转换(坑多)
"5" + 3    // "53"   字符串拼接
"5" - 3    // 2      数值减法
"5" * "2"  // 10
0 == ""    // true   ← 隐式转换导致
0 == false // true
null == undefined  // true

// 显式转换(推荐)
Number("42")   // 42
String(42)     // "42"
Boolean(0)     // false
parseInt("42px")  // 42
TIP

📌 为什么推荐 === 而不是 ==:== 会做隐式类型转换,规则复杂且反直觉(0 == "" 为 true)。=== 严格比较,类型不同直接返回 false,行为可预测。永远用 ===,除非你确切知道为什么要用 ==。


12 · 函数与作用域

12.1 函数声明方式

// 函数声明(提升)
function add(a, b) { return a + b; }

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

// 箭头函数(ES6)
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => "Hello";

// 立即执行函数 (IIFE)
(function() {
  console.log("立即执行");
})();

12.2 箭头函数 vs 普通函数

区别 普通函数 箭头函数
this 调用时决定 定义时继承外层
arguments 有 无
new 可构造 不可构造
yield 可做生成器 不可
const obj = {
  name: "Alice",
  greet: function() {
    // 普通函数:this 指向 obj
    setTimeout(function() {
      console.log(this.name);  // undefined(this 指向 window)
    }, 100);
  },
  greetArrow: function() {
    // 箭头函数:this 继承外层
    setTimeout(() => {
      console.log(this.name);  // "Alice"
    }, 100);
  }
};
TIP

📌 箭头函数为什么没有自己的 this:箭头函数设计初衷就是解决回调中 this 丢失的问题。以前需要 var self = this 或 .bind(this) 来保存上下文。箭头函数直接继承词法作用域的 this,代码更简洁。但这也意味着它不适合做对象方法(this 不会指向对象)。

12.3 闭包

function counter() {
  let count = 0;
  return function() {
    return ++count;
  };
}

const c = counter();
c();  // 1
c();  // 2
c();  // 3
// count 变量被闭包"捕获",外部无法直接访问,但内部函数可以读写
TIP

📌 闭包的本质:函数执行完毕后,其局部变量本应被回收。但如果内部函数被返回并持有对外部变量的引用,这些变量就不会被回收——这就是闭包。它像函数的"记忆",让函数能记住自己被创建时的环境。模块模式、柯里化、防抖节流都依赖闭包。


13 · 对象与原型

13.1 对象基础

// 对象字面量
const user = {
  name: "Alice",
  age: 30,
  greet() { return `Hi, I'm ${this.name}`; }
};

// 属性简写(ES6)
const name = "Bob";
const obj = { name };  // 等同 { name: name }

// 计算属性名
const key = "dynamic";
const obj2 = { [key]: "value" };  // { dynamic: "value" }

// 可选链
const city = user?.address?.city;  // 安全访问嵌套属性

// 空值合并
const name = user.name ?? "Anonymous";  // null/undefined 时用默认值

13.2 原型链

function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function() {
  return `${this.name} makes a sound`;
};

function Dog(name) {
  Animal.call(this, name);  // 调用父构造函数
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
  return `${this.name} barks!`;
};

const d = new Dog("Rex");
d.speak();  // "Rex makes a sound"(继承自 Animal)
d.bark();   // "Rex barks!"(自有方法)

13.3 ES6 Class

class Animal {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} makes a sound`; }
}

class Dog extends Animal {
  constructor(name) {
    super(name);  // 必须在 this 之前调用
  }
  bark() { return `${this.name} barks!`; }
}
TIP

📌 Class 是语法糖:ES6 的 class 底层仍然是原型链继承,只是语法更接近传统面向对象语言。extends = Object.create(parent.prototype),super = Parent.call(this)。理解原型链才能真正理解 JS 的继承机制。


14 · 异步编程

14.1 为什么需要异步

JS 是单线程的——同一时间只做一件事。网络请求、文件读取等耗时操作如果同步等待,整个页面会卡死。异步编程让 JS 在等待时能处理其他任务。

14.2 回调 → Promise → async/await

// ① 回调地狱
getUser(id, function(user) {
  getOrders(user, function(orders) {
    getItems(orders, function(items) {
      console.log(items);
    });
  });
});

// ② Promise 链
getUser(id)
  .then(user => getOrders(user))
  .then(orders => getItems(orders))
  .then(items => console.log(items))
  .catch(err => console.error(err));

// ③ async/await(推荐)
async function loadData() {
  try {
    const user = await getUser(id);
    const orders = await getOrders(user);
    const items = await getItems(orders);
    console.log(items);
  } catch (err) {
    console.error(err);
  }
}
TIP

📌 async/await 的本质:它不是新的异步机制,而是 Promise 的语法糖。await 暂停函数执行等待 Promise resolve,async 函数自动返回 Promise。它让异步代码看起来像同步代码,消除了回调嵌套和 .then() 链的可读性问题。

14.3 Promise 常用方法

// 并行执行,全部完成
const [users, posts] = await Promise.all([getUsers(), getPosts()]);

// 并行执行,任一完成
const fastest = await Promise.race([fetch(url1), fetch(url2)]);

// 并行执行,全部完成(无论成功失败)
const results = await Promise.allSettled([p1, p2, p3]);
// [{status:"fulfilled", value:...}, {status:"rejected", reason:...}]

15 · DOM 操作

15.1 选择元素

document.getElementById("id")
document.querySelector(".class")      // CSS 选择器,返回第一个
document.querySelectorAll("div p")    // 返回所有匹配

15.2 修改元素

const el = document.querySelector("#title");

// 内容
el.textContent = "新标题";       // 纯文本(安全)
el.innerHTML = "<b>标题</b>";    // HTML(注意 XSS)

// 属性
el.setAttribute("data-id", "42");
el.dataset.id;  // "42"(data-* 属性)

// 样式
el.style.color = "red";
el.classList.add("active");
el.classList.remove("hidden");
el.classList.toggle("open");

// 创建与插入
const newEl = document.createElement("p");
newEl.textContent = "新段落";
el.appendChild(newEl);          // 添加子元素
el.remove();                    // 删除自身
TIP

📌 textContent vs innerHTML:innerHTML 解析 HTML 标签,如果内容来自用户输入,可能导致 XSS 攻击。textContent 只处理纯文本,标签会被转义显示。用户内容永远用 textContent。


16 · 事件机制

16.1 事件监听

const btn = document.querySelector("#btn");

btn.addEventListener("click", function(e) {
  console.log("被点击了", e.target);
});

// 事件委托:利用冒泡,在父元素监听
document.querySelector("#list").addEventListener("click", e => {
  if (e.target.matches("li")) {
    console.log("点击了", e.target.textContent);
  }
});
TIP

📌 事件委托的好处:100 个 <li> 不需要绑 100 个监听器——在父元素 <ul> 上绑一个,利用事件冒泡统一处理。动态添加的 <li> 也能自动响应,不需要重新绑定。这是性能和可维护性的最佳实践。

16.2 事件流

捕获阶段:document → ... → 目标元素的父元素 目标阶段:目标元素 冒泡阶段:目标元素的父元素 → ... → document
// 第三个参数:true=捕获阶段触发,false=冒泡阶段(默认)
el.addEventListener("click", handler, false);

// 阻止冒泡
e.stopPropagation();

// 阻止默认行为
e.preventDefault();

17 · 模块化

17.1 ES Module

// math.js — 导出
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default function multiply(a, b) { return a * b; }

// main.js — 导入
import multiply, { PI, add } from "./math.js";
import * as math from "./math.js";  // 全部导入

17.2 动态导入

// 按需加载(懒加载)
const module = await import("./heavy-module.js");
module.doSomething();
TIP

📌 ES Module vs CommonJS:ESM 是浏览器原生支持的模块系统(import/export),静态分析可在编译期确定依赖关系(支持 Tree Shaking)。CommonJS(require/module.exports)是 Node.js 的模块系统,动态加载,不支持 Tree Shaking。现代前端开发统一用 ESM。


18 · 浏览器原理

18.1 渲染流程

HTML → DOM 树 → 渲染树 → 布局 → 绘制 → 合成 CSS → CSSOM 树
阶段 说明
解析 HTML 构建 DOM 树
解析 CSS 构建 CSSOM 树
渲染树 DOM + CSSOM 合并,排除 display:none 的元素
布局 (Layout) 计算每个元素的位置和大小
绘制 (Paint) 将渲染树转为屏幕像素
合成 (Composite) GPU 合成各图层
TIP

📌 重排 vs 重绘:改变几何属性(width/height/margin)触发重排(Layout → Paint → Composite 全流程)。改变视觉属性(color/background)只触发重绘(跳过 Layout)。改变 transform/opacity 只触发合成(GPU 层,最快)。性能优化的核心:减少重排,用 transform 代替位置变化。


19 · 性能优化

19.1 加载优化

优化 方法
减少请求 合并 CSS/JS、雪碧图、内联关键 CSS
压缩 Gzip/Brotli 压缩、图片 WebP/AVIF
缓存 HTTP 缓存头(Cache-Control/ETag)
懒加载 loading="lazy"(图片)、动态 import()(JS)
预加载 <link rel="preload"> 关键资源
CDN 静态资源分发到全球节点

19.2 运行时优化

// 防抖:连续触发只执行最后一次
function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

// 节流:固定频率执行
function throttle(fn, interval) {
  let last = 0;
  return function(...args) {
    const now = Date.now();
    if (now - last >= interval) {
      last = now;
      fn.apply(this, args);
    }
  };
}

// 使用:搜索输入防抖、滚动事件节流
input.addEventListener("input", debounce(search, 300));
window.addEventListener("scroll", throttle(onScroll, 100));
TIP

📌 防抖 vs 节流:防抖 = "等你说完我再做"(搜索框输入,停止 300ms 后才搜索)。节流 = "固定间隔做一次"(滚动事件,每 100ms 处理一次,不管滚多快)。两者都减少高频事件的执行次数,选择取决于场景。


20 · Web API 速览

20.1 常用 Web API

API 用途
fetch() 网络请求(替代 XMLHttpRequest)
localStorage / sessionStorage 本地存储
IntersectionObserver 元素可见性监听(懒加载)
MutationObserver DOM 变化监听
WebSocket 双向实时通信
Canvas / WebGL 2D/3D 绘图
Web Workers 后台线程
Service Worker 离线缓存、推送通知

20.2 Fetch 示例

// GET
const res = await fetch("/api/users");
const users = await res.json();

// POST
const res = await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Alice" })
});
const data = await res.json();

附录 A · HTML 标签速查

标签 说明
<div> 通用块级容器
<span> 通用行内容器
<a href=""> 链接
<img src="" alt=""> 图片
<ul>/<ol>/<li> 列表
<table>/<tr>/<td>/<th> 表格
<form>/<input>/<select>/<textarea> 表单
<button> 按钮
<video>/<audio> 媒体
<canvas> 画布
<iframe> 内嵌框架
<header>/<nav>/<main>/<footer> 语义化结构

附录 B · CSS 属性速查

属性 说明 常用值
display 显示方式 block inline flex grid none
position 定位 static relative absolute fixed sticky
margin/padding 外/内边距 px em auto
border 边框 1px solid #ccc
border-radius 圆角 px %
box-shadow 阴影 0 2px 8px rgba(0,0,0,0.1)
color 文字颜色 #333 rgb() hsl()
background 背景 url() linear-gradient()
font 字体 16px/1.5 sans-serif
text-align 文字对齐 left center right justify
z-index 层叠顺序 数字
overflow 溢出处理 visible hidden auto scroll
transition 过渡 all 0.3s ease
transform 变换 translate() rotate() scale()
opacity 透明度 0~1

附录 C · JS 内置对象速查

对象/方法 说明
Array.map/filter/reduce 数组变换/过滤/聚合
Array.forEach 遍历
Array.find/findIndex 查找
Array.includes 包含判断
Array.flat/flatMap 扁平化
Object.keys/values/entries 对象键/值/键值对
Object.assign 合并对象
Object.freeze 冻结对象
JSON.parse/stringify JSON 解析/序列化
String.split/replace/match 字符串操作
String.trim/padStart/padEnd 字符串修剪/填充
Math.max/min/floor/ceil/random 数学运算
Date.now/new Date() 日期时间
Map/Set 映射/集合(ES6)
Promise 异步编程
console.log/error/warn 调试输出

附录 D · 调试技巧

技巧 方法
Console console.log() console.table() console.group()
断点 DevTools → Sources → 点击行号
条件断点 右键行号 → Add conditional breakpoint
DOM 检查 F12 → Elements 面板
网络分析 F12 → Network 面板
性能分析 F12 → Performance 面板 → Record
移动端调试 F12 → Toggle device toolbar
debugger 代码中插入 debugger 语句自动断点
$0 DevTools 中引用最后选中的元素