📚 全栈开发学习系列 · 阶段二(Web 全栈)
09 HTML 基础:网页骨架与语义化标签 ✅
10 CSS 基础:样式、布局与响应式 ✅
11 JavaScript 基础:变量、DOM 与事件(当前篇)
12 HTTP 协议基础:请求、响应与状态码
13 FastAPI 入门:构建第一个 RESTful API
14 PostgreSQL 基础:数据库设计与 SQL 操作
15 认证授权:JWT、Session 与 Cookie
16 综合实战:带登录的博客系统

JavaScript 基础:变量、DOM 与事件

难度:基础 | 前端三剑客的最后一块拼图,让网页从静态展板进化为交互式应用
读完本篇你将能:用 let 和 const 声明变量并理解五种数据类型,编写函数处理逻辑,用 querySelector 选取并修改网页元素,通过 addEventListener 响应用户的点击和输入,最终为第 9-10 篇的个人主页加上暗色模式切换和动态待办列表。
📑 本文目录
01JavaScript 初识:给网页装上神经系统
02变量与数据类型
03运算符与表达式
04函数:封装可复用的逻辑
05DOM 操作:用代码操控网页
06事件处理:响应用户操作
07综合实战:交互式个人主页

01 JavaScript 初识:给网页装上神经系统

前两篇里,HTML 搭出了网页的骨架,CSS 给骨架穿上了衣服。但页面始终是静止的——用户点击按钮没反应,填写表单没反馈。如果把网页比作人体,HTML 是骨骼,CSS 是皮肤,那 JavaScript 就是神经系统:它感知用户的触摸,控制肌肉的收缩,让整个身体活过来。

三种引入方式

和 CSS 类似,JavaScript 也有三种引入方式,但推荐程度大不相同:

方式 写在哪里 推荐度
行内式 HTML 标签的 onclick 属性 不推荐
内嵌式 <script> 标签放在 HTML 中 学习阶段推荐
外链式 <script src="app.js"> 项目实战推荐

学习阶段用内嵌式最方便——代码和 HTML 在同一文件里,保存即刷新。下面是第一个 JS 程序:

HTML + JavaScript
<script>
  // 在浏览器控制台输出文字
  console.log("Hello, JavaScript!");
  // 弹出提示框
  window.alert("欢迎学习全栈开发!");
</script>

把这段代码放进 HTML 的 <body> 末尾,用浏览器打开后:控制台(按 F12 打开开发者工具 → Console 标签页)会打印 Hello, JavaScript!,同时屏幕弹出一个写着"欢迎学习全栈开发!"的提示框。

💡 小贴士
console.log() 是开发调试最常用的工具。看到代码没按预期运行?在关键位置插入一行 console.log(变量名) 查看实际值,比盲目猜测高效得多。

02 变量与数据类型

你已经会了 Python 的变量,JS 的概念类似但声明方式不同。JavaScript 用三个关键字声明变量:let、const 和老旧的 var。

let vs const vs var

关键字 能否重新赋值 使用场景
const 不能 默认首选,值不变的量
let 能 需要改变值的计数器、循环变量
var 能 已过时,现代代码不推荐使用
JavaScript
const PI = 3.14159;  // 圆周率,不会变
let count = 0;        // 计数器,会变
count = count + 1;  // ✅ let 变量可以重新赋值
// PI = 3.0; // ❌ TypeError: Assignment to constant
console.log(count);  // 输出: 1

五种基本数据类型

JavaScript 有五种基本数据类型,加上两种复合类型(数组和对象),覆盖了日常开发绝大多数场景:

类型 示例 说明
String "hello" 文本,单引号或双引号均可
Number 42, 3.14 整数和浮点数统一为 Number
Boolean true, false 只有两个值,条件判断的核心
null null 开发者主动赋的"空值"
undefined undefined 变量声明了但没赋值,系统默认值

用 typeof 运算符可以检测变量的类型——这是调试类型相关 bug 的第一步:

JavaScript
1
2
3
4
5
6
7
8
9
const name = "张三";
const age = 25;
const isStudent = true;
let empty = null;
let nothing;  // 声明未赋值
console.log(typeof name);     // "string"
console.log(typeof age);     // "number"
console.log(typeof empty);    // "object"(已知设计缺陷)
⚠️ 常见错误
typeof null 返回 "null" — 这是 JavaScript 的历史遗留设计缺陷,typeof null 实际返回 "object"。判断 null 要用 === null 而非 typeof。
✓ 正确:if (value === null) { ... }

数组与对象(复合类型)

单个变量存单个值,多个值怎么办?数组和对象是两种最常用的数据容器:

JavaScript
1
2
3
4
5
6
7
8
9
10
// 数组:有序的数据集合
const skills = ["Python", "C", "JavaScript"];
console.log(skills[0]);  // "Python"(从0开始)
console.log(skills.length);  // 3
// 对象:键值对集合
const user = {
  name: "张三",
  age: 25,
  isOnline: true
};

读取对象属性用点号:user.name 得到 "张三"。读取数组元素用索引:skills[1] 得到 "C"。这两种数据结构在后续 DOM 操作和 API 调用中会反复出现。

03 运算符与表达式

变量存了值,运算符把值加工成新结果。算术运算符和 Python 几乎一样,但比较运算符有一个关键陷阱需要特别留意。

算术与字符串拼接

JavaScript
const a = 10, b = 3;
console.log(a + b);  // 13(加法)
console.log(a % b);  // 1(取余)
// + 遇到字符串就变拼接
console.log("年龄:" + 25);  // "年龄:25"
console.log(`2 + 2 = ${2 + 2}`);  // "2 + 2 = 4"
console.log(`2 + 2 = ${2 + 2}`);

最后一行用了模板字符串——反引号包裹的字符串中,${表达式} 会被自动替换为表达式的值。这比 Python 的 f-string 语法稍长,但功能相同,在拼接 HTML 片段时特别方便。

比较运算符:=== vs ==

这是 JavaScript 最经典的坑:== 会做类型转换再比较,=== 严格比较类型和值。永远用 ===,没有例外:

JavaScript
console.log(0 == "");      // true(== 会做类型转换)
console.log(0 === "");    // false(=== 严格比较)
console.log(1 == "1");     // true
console.log(1 === "1");   // false
// 逻辑运算符:&& (且) || (或) ! (非)
const canVote = age >= 18 && hasID;  // 两个都为true才行
⚠️ 常见错误
if (status == "active") — 用 == 时,数字 0 和空字符串 "" 会被视为相等,导致意外的条件分支被触发。
✓ 正确:if (status === "active")

04 函数:封装可复用的逻辑

学会了变量和运算符,你已经能写顺序执行的代码了。但当代码超过二十行,重复逻辑会开始堆积。函数把一段逻辑打包,取个名字,反复调用——和 Python 的 def 概念一致,语法不同。

函数声明

JavaScript
1
2
3
4
5
6
7
8
9
10
// 计算商品折后价格
function calcPrice(price, discount) {
  const finalPrice = price * (1 - discount);
  return finalPrice;toFixed(2);
}
// 调用函数
console.log(calcPrice(199, 0.3));  // "139.30"
console.log(calcPrice(50, 0.1));   // "45.00"

toFixed(2) 是 Number 的方法,把数字保留两位小数并返回字符串形式,在显示价格时很常用。

箭头函数:更简洁的写法

现代 JavaScript 中,函数经常用"箭头函数"语法定义,特别是作为参数传给其他函数时(回调),写法更紧凑:

JavaScript
// 传统函数声明
function greet(name) { return `你好,${name}`; }
// 等价的箭头函数
const greet = name => `你好,${name}`;
console.log(greet("张三"));  // "你好,张三"

箭头函数的规则:参数只有一个时省略括号,函数体只有一行 return 时省略大括号和 return 关键字。后续的事件处理章节会大量使用箭头函数,因为它的 this 指向更直观(这个特点在复杂项目中很重要,本篇暂不展开)。

05 DOM 操作:用代码操控网页

前面四章学的 JS 语法,和 Python 一样是在控制台里跑的。JavaScript 真正的威力在于:它能直接操作网页上的每一个元素。浏览器加载 HTML 后,会把每个标签变成一个可编程的对象,这些对象组成的树状结构就叫 DOM(Document Object Model,文档对象模型)。

选取元素

操作 DOM 的第一步是"找到"目标元素。两个核心方法覆盖 90% 的场景:

JavaScript
1
2
3
4
5
6
7
8
9
// 通过 ID 选取(返回单个元素)
const title = document.getElementById("main-title");
// 通过 CSS 选择器选取(推荐)
const card = document.querySelector(".card");
const firstLi = document.querySelector("ul li");
// 选取所有匹配的元素(返回 NodeList)
const allCards = document.querySelectorAll(".card");
console.log(allCards.length);  // 匹配到的元素数量

querySelector 接受的参数和 CSS 选择器完全一样——"#id"、".class"、"div > p" 都行。你在第 10 篇学的 CSS 选择器知识直接复用。

修改内容与样式

拿到元素后,可以修改它的文字内容、HTML 结构和 CSS 样式:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
const title = document.querySelector("#title");
// 修改纯文字内容(安全,推荐)
title.textContent = "新标题";
// 修改 HTML 内容(可插入标签)
title.innerHTML = "<span>高亮标题</span>";
// 修改 CSS 样式(驼峰命名,不用连字符)
title.style.color = "#d97706";
title.style.fontSize = "24px";  // 不是 font-size
// 添加/移除 class
title.classList.add("highlight");
title.classList.toggle("dark-mode");  // 有则移除,无则添加
💡 小贴士
JS 修改 CSS 属性时,连字符属性名要改成驼峰式:font-size → fontSize,background-color → backgroundColor。这是 JS 语法的要求,连字符会被解释为减号。

创建与插入元素

除了修改已有元素,还能用代码动态创建新元素并插入页面——这是实现动态列表、实时评论等功能的基础:

JavaScript
// 创建一个 <li> 元素
const item = document.createElement("li");
item.textContent = "学习 JavaScript";
item.style.color = "#d97706";
// 找到父元素 <ul>,把 <li> 追加进去
const list = document.querySelector("#todo-list");
list.appendChild(item);  // 页面上立刻出现新条目

三步走:创建元素 → 设置内容/样式 → 插入到父元素。这个模式在后文的综合实战中会反复使用。

06 事件处理:响应用户操作

上一章你学会了修改网页,但修改是"主动"的——代码一跑就改了。真实场景中,修改应该由用户触发:点击按钮、输入文字、提交表单。事件监听就是连接"用户操作"和"代码响应"的桥梁。

addEventListener:监听用户行为

JavaScript
1
2
3
4
5
6
7
8
9
10
const btn = document.querySelector("#myBtn");
// 参数1:事件类型 参数2:回调函数
btn.addEventListener("click", () => {
  console.log("按钮被点击了!");
});
// 监听输入框内容变化
const input = document.querySelector("#name-input");
input.addEventListener("input", (e) => {
  console.log(`当前输入:${e.target.value}`);

回调函数的参数 e(event 对象)携带了事件的详细信息:e.target 是触发事件的元素,e.target.value 是输入框当前的值。

常用事件速查

事件名 触发时机 典型用途
click 鼠标点击元素 按钮提交、切换状态
input 输入框内容变化 实时字数统计、搜索建议
submit 表单提交 登录/注册表单验证
DOMContentLoaded HTML 解析完成 初始化页面逻辑
⚠️ 常见错误
script 放在 head 中,直接 querySelector 取元素返回 null — 浏览器从上到下解析 HTML,如果 <script> 在 <body> 内容之前执行,元素还不存在。
✓ 正确:把 <script> 放在 </body> 之前,或用 DOMContentLoaded 事件等待 DOM 加载完成。

07 综合实战:交互式个人主页

现在把前面六章的知识串联起来。我们在第 9-10 篇的个人主页基础上,添加两个交互功能:暗色模式切换和动态待办列表。最终效果:点击按钮在亮/暗主题间切换,输入框输入文字按回车添加待办项,点击待办项可删除。

下面是完整的 HTML 文件(内嵌 CSS 和 JS),保存为 interactive-home.html 后用浏览器打开即可体验:

HTML + CSS + JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<body id="page">
  <button id="themeBtn">切换主题</button>
  <h1 id="title">我的个人主页</h1>
  <input id="todoInput" placeholder="输入待办,按回车添加">
  <ul id="todoList"></ul>
<script>
  // 功能1:暗色模式切换
  const page = document.getElementById("page");
  let isDark = false;
  document.getElementById("themeBtn")
    .addEventListener("click", () => {
      isDark = !isDark;  // 切换布尔值
      page.style.backgroundColor = isDark ? "#1a1a2e" : "#ffffff";
      page.style.color = isDark ? "#e0e0e0" : "#333333";
    });
  // 功能2:动态待办列表
  const input = document.getElementById("todoInput");
  const list = document.getElementById("todoList");
  input.addEventListener("keydown", (e) => {
    // 按下回车键且输入框非空
    if (e.key === "Enter" && input.value.trim() !== "") {
      const item = document.createElement("li");
      item.textContent = input.value;
      item.style.cursor = "pointer";
      // 点击待办项删除自己
      item.addEventListener("click", () => {
        item.remove();
      });
      list.appendChild(item);
      input.value = "";  // 清空输入框
    }
  });
</script>
</body>

这段代码整合了本篇所有核心知识点:

• 第 9-11 行:let 声明可变状态 isDark,用 ! 取反切换布尔值
• 第 14-15 行:三元运算符 条件 ? A : B,等价于 Python 的 A if 条件 else B
• 第 24 行:e.key === "Enter" 严格比较,.trim() 去除首尾空格防止空输入
• 第 25-30 行:创建元素 → 设置内容 → 绑定点击删除事件 → 追加到列表,三步走完整呈现
💡 小贴士
item.remove() 是现代浏览器支持的元素自删除方法。如果需要兼容旧浏览器,可以用 list.removeChild(item) 替代,效果相同。
📖 知识回顾
let / const 声明 五种基本类型 === 严格比较 模板字符串 箭头函数 querySelector 选取 textContent / innerHTML style 驼峰命名 createElement 创建 addEventListener 监听 event.target classList.toggle
✏️ 动手练习
🟢 基础验证
请声明一个 const 对象 book,包含 title(字符串)、price(数字)、inStock(布尔)三个属性,然后用 console.log 输出 typeof book.price 的结果。
参考解法:const book = { title: "JS入门", price: 59.9, inStock: true }; console.log(typeof book.price); 输出 "number"
🟡 组合应用
在综合实战代码的基础上,添加一个 <p id="counter"> 元素,统计并显示当前待办项数量。提示:每次添加或删除待办项时,用 list.children.length 获取列表子元素数量,更新 textContent。
参考方向:在 addItem 和 removeItem 后调用一个 updateCounter() 函数,函数内写 counter.textContent = "当前待办:" + list.children.length + " 项";
🔴 开放挑战
将暗色模式的状态保存到浏览器的 localStorage 中,使页面刷新后仍保持上次选择的主题。提示:搜索关键词 "MDN localStorage setItem getItem" 了解用法。核心思路——切换主题时 localStorage.setItem("theme", "dark"),页面加载时 localStorage.getItem("theme") 读取并应用。
下篇预告
12 HTTP 协议基础:请求、响应与状态码
前端三剑客已集齐,下一步走向后端——理解浏览器和服务器之间的通信协议,掌握 GET/POST 请求、HTTP 状态码和请求头,为学习 FastAPI 打下基础
关注公众号 · 持续获取全栈开发学习更新