脚本顺序决定全局变量能否被读取及报错时机:var存在提升但跨脚本不传递,let/const因tdz跨脚本必报错,函数声明可跨脚本调用但执行时机需谨慎,动态插入脚本拥有独立上下文。

脚本顺序直接决定全局变量能不能被读到、什么时候变成 undefined、为什么 let 声明比 var 更容易报错——这不是“执行慢一点”的问题,而是变量根本没被初始化或还没进入可访问状态。
多个 <script></script> 块中 var 变量为什么有时是 undefined
因为变量提升只发生在每个脚本块自己的“创建阶段”,而赋值在“执行阶段”;两个阶段被脚本加载顺序严格切割。浏览器不会把前一个 <script></script> 的提升结果“传给”后一个。
-
<script>var a = 1;</script>在前,<script>console.log(a);</script>在后 → 输出1 - 顺序颠倒 → 第二个脚本执行时,
a还没声明,报ReferenceError: a is not defined - 哪怕第一个脚本里写的是
var a;(没赋值),第二个脚本读取仍是undefined,不是ReferenceError
let 和 const 在跨 <script></script> 场景下会直接报错
let/const 不提升,且存在暂时性死区(TDZ)。即使声明语句物理位置靠前,只要执行时机晚于读取,就一定触发 ReferenceError。
-
<script>let a = 1;</script>和<script>console.log(a);</script>顺序不能调换,调换必报错 - 不能靠“先声明后使用”的直觉判断,必须确保读取代码在声明代码执行之后
- 函数作用域内用
let不影响全局,但全局块级作用域的let就是硬性隔离
函数声明能跨 <script></script> 调用,但执行时机仍可能出问题
function foo() {} 会被完整提升到全局上下文创建阶段,挂载到 window 上,所以只要它所在的 <script></script> 已加载完毕(哪怕还没执行到函数体),后续脚本就能调用。
- 前一个
<script></script>写function init() { ... },后一个可直接init() - 但如果写成
const init = () => {}或var init = function() {},则只提升变量名,init是undefined,调用报TypeError - 函数虽可调用,若内部依赖
document.body,而调用发生在解析前,仍会拿到null
动态插入的 <script></script> 不继承前序脚本的执行上下文
用 document.createElement('script') 插入的脚本,会触发一次新的、独立的全局执行上下文创建流程——它有自己的变量提升阶段,不感知前序脚本中尚未执行的赋值语句。
- 你以为插进去就等于接在上一个
<script></script>后面,结果发现data突然变undefined - 默认行为等价于加了
async:下载不阻塞,执行不保序,无法靠 DOM 位置控制依赖 - 想保证顺序,得显式设
script.async = false(慎用,会阻塞)或封装成Promise链式加载
真正难处理的不是“怎么让变量出现”,而是“怎么让变量在正确的时间点以正确的值存在”。尤其当多个脚本共享状态、或依赖 DOM 就绪时机时,仅靠物理顺序远远不够,必须结合执行时机、作用域规则和加载策略一起判断。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











