根本原因是执行顺序错乱与作用域隔离:未加 defer/async 的脚本同步阻塞解析,依赖项若后加载或被 async 打乱顺序,导致调用时变量未定义;第三方脚本还可能污染全局命名空间引发冲突。

为什么 index.html 引入多个 JS 会报错“ReferenceError: XXX is not defined”
根本原因不是“加载失败”,而是执行顺序和作用域混乱。比如你先在 <script src="utils.js"></script> 里定义了 formatDate,又在内联脚本里直接调用它,但浏览器还没执行完 utils.js 就跑到了下面的代码——尤其当没加 defer 或 async,且 utils.js 放在 <body> 底部时,这个时机差就更明显。
常见触发场景:
-
index.html中<script>标签顺序颠倒(依赖项在被依赖项之后) - 混用
async和非async脚本:带async的会并行下载、一加载完就执行,完全不等 DOM 或其他脚本 - 第三方 SDK(如百度统计、微信 JS-SDK)和你自己写的初始化逻辑抢着操作
window上的同名变量
defer 和 async 到底该选哪个?
二者都影响脚本执行时机,但行为完全不同,选错等于埋雷。
-
defer:下载异步,执行严格按 HTML 中的书写顺序,且等到 DOM 解析完成(DOMContentLoaded前)才执行 —— 适合有依赖关系的业务脚本,比如jquery.js必须在app.js之前 -
async:下载异步,**下载完立刻执行**,不保证顺序,也不等 DOM —— 只适合完全独立、无依赖、不操作 DOM 的脚本,比如埋点、广告 SDK - 没写任何属性的
<script src="..."></script>:同步阻塞,会卡住 HTML 解析,优先级最低,除非兼容极老环境,否则别用
示例:正确组织依赖链
立即学习“前端免费学习笔记(深入)”;
<script defer src="lodash.js"></script> <script defer src="utils.js"></script> <script defer src="main.js"></script>
如何快速定位哪个 JS 导致了 ReferenceError 或 TypeError?
别靠猜。打开浏览器 DevTools → Console,点错误信息右侧的文件链接,直接跳到报错行;再看堆栈(Stack Trace),从下往上读,找到最早触发的那一行 JS 是谁加载的。
- 如果报错出现在
main.js:42,但main.js里第 42 行是initApp(config),而initApp是从core.js挂到window上的——那就说明core.js没加载或没执行成功 - 检查 Network 面板:筛选
JS,看对应脚本状态码是不是404或500,或者响应体为空(常见于路径写错、本地服务器没配 MIME 类型) - 在关键脚本开头加
console.log('core.js loaded'),确认是否真的执行了
第三方脚本和自有脚本冲突怎么办?
典型表现是:单独引入某 SDK 没问题,但和你的 auth.js 一起用,就报 Cannot read property 'on' of undefined。大概率是双方都试图覆盖 window.wx、window.BMap 这类全局命名空间。
- 查文档确认该 SDK 是否支持模块化加载(如微信 JS-SDK 2.0+ 推荐用
import+ 构建工具,而非直接挂window.wx) - 手动隔离:用 IIFE 包一层,避免污染全局,例如
(function(){ var wx = window.wx; /* 在这里用 wx */ })(); - 加载前检测:在引入自家脚本前,先判断
if (typeof window.wx !== 'undefined'),再决定是否 patch 或跳过初始化 - 路径冲突:比如两个脚本都通过
document.write插入同一<div id="container">,结果后加载的把前一个的内容清空了——改用document.getElementById('container').appendChild(...)更安全
最麻烦的情况是:某个脚本内部用了 eval 或动态 Function 构造器,又依赖特定全局变量。这种没法靠加载顺序解决,只能联系 SDK 提供方或换方案。


















