JavaScript 的加载与变量
上一节在 Console 中计算出了人数和平均分。现在需要把程序保存下来,修改数据后只需刷新页面,就能再次看到结果。本节先完成“HTML 加载脚本 → 变量保存数据 → Console 输出”,再认识其他加载方式和模块拆分。
外部脚本
项目代码放入独立 .js 文件,再通过 script 的 src 属性加载:
<script src="./app.js" defer></script>推荐目录:
js-basic/
├── index.html
└── app.js<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript 基础</title>
<script src="./app.js" defer></script>
</head>
<body>
<h1>请打开浏览器控制台</h1>
</body>
</html>console.log("JavaScript 文件加载成功");保存两个文件后打开 index.html,在 Console 中应看到“JavaScript 文件加载成功”。这一步只确认 HTML 找到了脚本;接下来把上一节的学员数据放进 app.js,计算一次报名后的剩余名额。
这里的 ./app.js 相对于当前 HTML 文件。script 不是空元素,必须写结束标签。
defer 的作用
对于普通外部脚本,defer 让浏览器在继续解析 HTML 的同时下载脚本,并在文档解析完成后按顺序执行。
本部分的普通外部脚本使用 defer,为后续 DOM 学习建立稳定习惯。模块脚本会在本节后半部分单独说明。
另一种基础写法是把脚本放在 body 结束标签之前:
<script src="./app.js"></script>
</body>本部分的普通脚本案例统一采用 head 中的 defer,不要求学员在不同加载位置之间反复切换。
变量与报名数据
变量是数据的“命名存储”。学员姓名、课程名称、人数和状态都可以先保存到变量中,再参与后续计算。
const 与 let
const:无需重新赋值的数据
const courseName = "FastAPI Web 全栈";
const maxStudents = 30;变量绑定后不需要重新赋值时,优先使用 const:
// courseName = "Python 基础"; // TypeErrorconst 要求声明时立即提供初始值。
let:需要重新赋值的数据
确实需要重新赋值时使用 let:
let currentCount = 28;
currentCount = currentCount + 1;
console.log(currentCount); // 29把上段的 currentCount = currentCount + 1; 替换为复合赋值,输出仍为 29:
currentCount += 1;let 适合计数器、循环变量或随业务状态变化的结果。
var:旧版声明方式
旧代码中会看到:
var studentName = "张三";var 的函数作用域和提升行为更容易造成误解。现代课程统一使用 const 和 let,但要能识别历史代码中的 var。
报名摘要
回到最初的 index.html 和 app.js,将普通脚本的内容替换为:
const className = "Web 前端 1 班";
const capacity = 30;
let studentCount = 28;
studentCount += 1;
const remainingSeats = capacity - studentCount;
console.log("班级:", className);
console.log("当前人数:", studentCount);
console.log("剩余名额:", remainingSeats);输出人数为 29,剩余名额为 1。班级名称和容量不需要重新赋值,用 const;人数会变化,用 let。本节先用数字计算;p03c03 会处理来自文本输入的人数与成绩。
const 与对象
下面用一条学员记录预览对象写法:花括号内的 name 和 status 是字段。现在只需区分“修改记录的字段”和“给变量重新赋值”,对象的完整用法在 p03c05 学习。
const student = {
name: "张三",
status: "pending",
};
student.status = "active";
console.log(student);const 阻止把变量重新指向另一个值,不会自动冻结对象内部:
// student = { name: "李四" }; // 不允许重新赋值数组同理,可以 push(),但不能给变量整体重新赋值。
变量命名
推荐小驼峰:
const studentName = "张三";
const registerDate = "2026-07-16";
const isActive = true;命名建议:
- 使用含义完整的英文单词。
- 布尔值常用
is、has、can开头。 - 函数以后常用动词开头,如
getStudentCount;定义方式在函数章节学习。 - 避免
a、data1、temp2等模糊名称。 - 不使用保留字,如
class、const。
JavaScript 标识符可以包含 $ 和 _,但课程业务变量不需要刻意使用特殊字符。
语句与注释
语句与分号
回看报名摘要,const capacity = 30; 是变量声明语句,console.log("剩余名额:", remainingSeats); 是输出语句。它们按顺序执行。分号标明语句结束;为了便于逐行阅读,课程统一在完整语句末尾写分号。
有些代码省略分号也能运行,因为 JavaScript 在特定位置会自动插入分号,例如:
console.log("当前人数:", 29)
console.log("剩余名额:", 1)这里即使省略分号,两条输出语句仍会依次执行。但换行不一定结束语句,例如:
const remainingSeats = 30 -
(28 + 1);
console.log(remainingSeats); // 1减法表达式尚未写完,第二行仍是同一条声明语句。入门阶段无需记住所有插入规则,写清分号并让每条语句独占一行即可。
注释
注释用于说明代码中的业务意图,运行时不会输出。// 注释本行剩余内容,既能独占一行,也能写在语句后面:
// 先登记一名新学员,再计算剩余名额。
let studentCount = 28;
studentCount += 1; // 此时人数是 29
console.log("当前人数:", studentCount);需要连续说明几行时,可以用 /* 和 */ 包住多行注释:
/* 本例只演示人数变化。
学员数据改用数组统计时,再替换手写人数。 */
const capacity = 30;
console.log("容量:", capacity);Console 与排错
输出方式
const student = {
id: 1,
name: "张三",
status: "active",
};
console.log("当前学员:", student);
console.table([student]);
console.warn("这是一条警告示例");
console.error("这是一条错误示例");console.log():普通输出。console.table():表格形式查看数组或对象列表。console.warn():警告。console.error():错误信息。
这些方法用于开发调试,不要把控制台输出当作面向用户的界面提示。
浏览器弹窗
浏览器还提供 alert()、confirm() 和 prompt() 等弹窗:
// alert("保存成功");
// const accepted = confirm("确认删除吗?");
// const keyword = prompt("请输入学员姓名");它们是浏览器提供的 Web API,不是 ECMAScript 语法。这里先识别用途,不在 p03 使用弹窗处理业务。alert() 和 prompt() 不承担课程页面的正式提示与输入;p04 的待办清理和学员删除案例会用 confirm() 做简单二次确认。浏览器原生弹窗会阻断页面操作,正式页面若需要更灵活的反馈,可在学习 DOM 后实现页面内提示。
无输出排查
- 确认打开了正确 HTML 文件。
- 确认开发者工具 Console 没有过滤普通日志。
- 查看 Network 中
app.js是否加载成功。 - 检查
src相对路径和文件名大小写。 - 检查 Console 的第一条错误和对应行号。
- 确认修改后保存并刷新页面。
可以先把 index.html 的 src 临时改为 ./missing.js 并刷新:Network 中会出现脚本加载失败,app.js 的日志也不会出现。恢复为 ./app.js 后,若文件加载成功却仍没有预期结果,再到 Console 检查脚本内部错误。这个对照能区分“文件没加载”和“加载后执行出错”。
可以在文件第一行临时加入:
console.log("app.js 开始执行");常见错误
先看错误类型,再点开文件名和行号,回到相关代码查原因。下面的错误语句都保留为注释,练习时每次只取消一条注释,并在观察后恢复。
| 错误类型 | 常见原因 | 首先检查 |
|---|---|---|
SyntaxError | 括号、引号不成对,或同一作用域重复声明变量 | 标点与声明位置 |
ReferenceError | 变量未定义、名字拼错,或超出可访问范围 | 声明与变量名称 |
TypeError | 对当前值执行不允许的操作 | 值的类型和操作方式 |
const studentCount = 28;
// console.log("人数:", studentCount; // 缺少右括号
// console.log(studentcount); // 大小写不一致
// studentCount = 29; // const 不能重新赋值语法错误可能使整个脚本无法开始执行;运行过程中出现未处理的错误,则会中断当前执行流程。修复第一处错误后重新运行,再处理后续问题。console.error() 只是主动输出一条错误样式的日志,不等于抛出异常。
行内与内嵌脚本
完成外部脚本案例后,再认识阅读旧页面时可能遇到的两种写法。
行内事件脚本
<input type="button" value="点我试试" onclick="alert('Hello World')"/>行内脚本把代码写进 HTML 事件属性,容易出现引号嵌套和维护困难。本节只要求能够识别,课程项目不采用这种写法;事件处理统一放在 p04。
内嵌脚本
<script>
console.log("内部脚本");
</script>它适合非常短的演示。可维护案例优先使用独立 .js 文件。
不要把 JavaScript 当成 script 的 src 属性内容,也不要在外部 .js 中再写 <script> 标签。
模块导入与导出
当一个页面只有一个脚本时,普通外部脚本已经够用;当数据逻辑和页面逻辑需要拆成多个文件时,可以使用 ES Module。模块文件默认拥有自己的作用域,文件之间通过 export 和 import 明确传递数据。
先把班级名称与平均分放入 data.js,再由入口脚本 app.js 读取。这里的 82.5 是用来观察模块传值的固定示例数据;p03c08 才会从学员数组计算平均分。
本部分按“普通外部脚本 → 最小模块示例 → 综合案例模块化”的顺序递进。前面的章节先使用单个 defer 脚本,避免学习语言语法时同时处理文件依赖;本节先认识最小的模块导入,p03c08 再把数据文件和入口文件正式拆开。进入 p04 后,在需要拆分文件的 DOM 和 Web API 案例中继续使用模块化写法。
<script type="module" src="./app.js"></script>三个文件放在同一个目录:
03-es-modules/
├── index.html
├── app.js
└── data.jsexport const className = "FastAPI Web 1 班";
export const averageScore = 82.5;import { averageScore, className } from "./data.js";
console.log(`${className}:平均分 ${averageScore}`);运行后 Console 应输出“FastAPI Web 1 班:平均分 82.5”。将 data.js 中的数值改为 90 并刷新,输出也应变成 90,说明入口脚本读到了导出数据。
type="module" 会让浏览器按模块方式加载脚本。这里的模块脚本默认会在 HTML 解析完成后执行,不必再添加 defer;模块之间的相对路径必须写清楚扩展名。不要依赖“先加载某个脚本,另一个脚本就能直接访问它的变量”这种隐式关系。
本节先理解最小的导出与导入;页面元素留到 p04 处理。下面的附件包含 HTML、入口脚本和数据模块。浏览器对 file:// 下的模块导入有限制,请在课程根目录运行 python -m http.server 5500,再打开 http://localhost:5500/.code/p03/c02-use/03-es-modules/。
如果页面没有输出,先在 Network 查看 app.js 和 data.js 是否加载成功;出现 404 时核对目录、文件名和 ./data.js 的扩展名。文件都成功加载却有导入错误时,再核对 import 的名称是否与 export 完全一致。直接双击打开导致模块请求受限时,改用上面的 HTTP 地址。
ES Module 最小示例源码
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ES Module 最小示例</title>
<script type="module" src="./app.js"></script>
</head>
<body>
<h1>请打开浏览器控制台</h1>
<p>app.js 从 data.js 导入班级名称和平均分。</p>
</body>
</html>import { averageScore, className } from "./data.js";
console.log(`${className}:平均分 ${averageScore}`);export const className = "FastAPI Web 1 班";
export const averageScore = 82.5;练习与验收
- 将初始人数改为
25,新增一人后应得到人数26、剩余名额4。 - 保留正确版本,单独练习一次变量名拼写错误,说明怎样根据 Console 的文件名和行号定位。
- 完成模块示例后,把导出的平均分改为
90,确认入口脚本能读取新值。 - 故意将
app.js中的导入路径写错一次,分别在 Console 与 Network 中找到线索,修复后确认输出恢复。
本节必须掌握普通脚本的加载、变量和 Console 排错;模块部分先掌握两个文件怎样明确传递数据,复杂文件组织留到后续案例。
小结
- 正式案例使用独立 JavaScript 文件,并通过相对路径加载。
- 普通外部脚本可使用
defer在 HTML 解析后执行。 - 默认使用
const,需要重新赋值时使用let。 const不会自动冻结对象和数组内容。- 控制台与网络面板可以区分加载错误和代码错误。