可靠执行需将script标签放在</body>之前,此时DOM已就绪,可直接操作元素;若放<head中,须用defer(外部脚本)或监听DOMContentLoaded事件。

script标签放哪儿执行才可靠
脚本是否执行,首要取决于 <script> 标签在 HTML 中的位置。浏览器自上而下解析,遇到 <script> 就立即下载、解析、执行(除非加了 async 或 defer)。如果脚本操作 DOM 元素,但元素还没解析到,document.getElementById 就会返回 null,常见报错是 Cannot read property 'xxx' of null。
可靠做法:
- 把
<script>放在</body>之前 —— 此时整个 DOM 已就绪,无需额外等待 - 或用
defer属性(仅适用于外部脚本):<script src="app.js" defer></script>,它会并行下载、按顺序执行,且保证在 DOM 解析完成之后运行 - 避免在
<head>里放同步脚本操作 DOM,除非你主动监听DOMContentLoaded
内联脚本 vs 外部脚本:加载行为差异大
内联脚本(写在 <script> 标签体内)和外部脚本(通过 src 引入)的执行时机和缓存行为完全不同。
关键区别:
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 内联脚本没有缓存,每次 HTML 加载都重新解析执行;外部脚本可被浏览器缓存,
src变化(如加版本号app.js?v=2)才强制更新 - 带
src的<script>标签,其内部内容(哪怕写了 JS)会被忽略 —— 不会执行,也不报错 -
async只对带src的有效,它让脚本异步下载、下载完立刻执行(可能打断 DOM 解析),不保证执行顺序
示例错误写法:<script src="util.js">console.log('this will NOT run');</script>
遇到“xxx is not defined”多半是作用域或执行顺序问题
这个错误不是语法错,而是变量或函数在当前作用域不可见。最常见于:
- 函数定义在某个
<script>块里,但调用在另一个块中,且后者先执行(JS 没有变量提升跨<script>块) - 用了
let/const声明,但访问发生在声明前(暂时性死区),尤其在内联脚本中容易踩坑 - 外部脚本用
async,导致它比依赖它的脚本更早执行,依赖未定义
调试建议:打开浏览器开发者工具,在 Sources 面板确认脚本加载顺序和执行时序;用 console.log 打点验证执行流,别只看报错行。
现代项目里 script 标签越来越“被动”
纯 HTML + <script> 的方式在工程化项目中基本只用于入口挂载(如 ReactDOM.render 或 createApp),逻辑主体早已移到模块系统里。这意味着:
-
type="module"成为新默认:它自动启用defer行为,且默认严格模式、支持import/export - ESM 脚本中顶层
this是undefined,不是window—— 如果旧代码依赖this指向全局,会出问题 - CDN 引入 ESM 库(如
https://unpkg.com/vue@3/dist/vue.esm-browser.js)必须配type="module",否则直接报错
真正容易被忽略的是:模块脚本里的相对路径导入(import { foo } from './utils.js')在直接双击 HTML 打开时会因 CORS 失败 —— 必须走本地服务器(如 npx serve)才能正常加载。


















