浏览器执行JavaScript脚本加载与代码执行顺序详解

宋时安
宋时安
发布时间:2026-10-14| 3人浏览过
原创

JavaScript在执行时具有阻塞特性,会暂停页面的解析、渲染及其他资源的下载。浏览器通常按照代码出现的从上到下顺序执行脚本,但这主要适用于标准的<script>标签引入。通过document.write、动态创建元素或AJAX异步获取脚本的方式,会打破这种固定的顺序,甚至引发跨浏览器的行为差异。

JavaScript的阻塞特性

JavaScript的解析和执行是串行的且具备阻塞性。当浏览器遇到JS代码时,会暂停HTML文档的解析、CSSOM的构建、页面渲染以及图片等其他外部资源的下载。这是因为JavaScript随时可能修改DOM结构,如果解析过程不等待脚本执行完毕,将导致DOM树的不稳定,进而引发极大的解析不可控风险。

对于外部引入的脚本文件,早期的浏览器不仅会阻塞页面解析,还会阻塞并行下载其他资源。但随着IE8、Firefox 3.5、Safari 4和Chrome 2等现代浏览器的普及,脚本文件的下载已经允许与其他资源(如CSS和图片)并行处理,极大地提升了加载性能。

标准引入方式的执行顺序

在不使用模块加载器(如requirejs或seajs)的前提下,标准引入方式是通过HTML中直接书写代码或在<script>标签中引入外部JS文件。在这种情况下,无论外部脚本是否采用了并行下载技术,**浏览器始终严格按照代码在文档流中出现的位置,从上到下依次执行**。

我们可以通过一个延迟加载的测试场景来验证这一点:

  1. 编写服务端延时接口:PHP脚本接收URL参数和延迟秒数,在返回前休眠指定时间。
  2. <?php
    $url = $_GET['url'];
    $delay = $_GET['delay'];
    if(isset($delay)){
        sleep($delay);
    }
    echo file_get_contents($url);
    ?>
  3. 定义两个JS文件:
    1.js输出“我是第一个脚本”;
    2.js输出“我是第二个脚本”。
  4. 设置不同的加载顺序:
    先通过接口引入1.js(设置延迟3秒),接着内联执行一段JS代码输出“我是内部脚本”,最后通过接口引入2.js(设置延迟1秒)。
<script src='/delayfile.php?url=.../1.js&delay=3' type='text/javascript'></script>
<script type="text/javascript">
alert("我是内部脚本");
</script>
<script src='/delayfile.php?url=.../2.js&delay=1' type='text/javascript'></script>

虽然2.js比1.js更快下载完成,但由于JS引擎必须等待前面的所有同步脚本执行完毕后才会去读取后续代码,最终的弹窗顺序依然是:“我是第一个脚本” → “我是内部脚本” → “我是第二个脚本”。这证实了标准引入方式下,执行顺序永远优先于下载速度。

通过 document.write 注入脚本的执行规则

document.write()方法直接向当前文档流中写入内容。若文档流尚未关闭,它会将字符串解析为真实的DOM节点插入到当前<script>标签结束后的紧邻位置。其执行顺序受写入位置和内容类型的深刻影响。

第1步:只写入外部脚本的引用

如果在同一块<script>代码中,使用document.write连续写入多个外部的<script src="...">,这些被动态写入的外部脚本,会在当前主脚本内的alert或计算语句完全执行完毕后才开始加载。一旦外部脚本下载并执行完,程序将继续往下走。

例如:
先输出“我是第一个脚本” (同步) → 接着进入内联JS → 执行write注入2.js(延迟1s) → write注入1.js(延迟2s) → 输出“我是内部脚本”。

在此过程中,“我是内部脚本”一定会出现在由write生成的脚本之前。而由于注入顺序是先写2再写1,且它们排在主脚本之后,因此DOM结构中位于下方的1.js尽管请求慢,还是会后执行,但因为是串行阻塞解析,最终呈现出的执行结果是:**“我是第一个脚本” → “我是内部脚本” → “我是第二个脚本” → “我是第一个脚本”**。

第2步:只写入内部匿名代码

若使用document.write写入的是内联JavaScript(如<script>alert('')</script>),这部分代码的执行优先级等同于该document.write所在的行。**它在write语句执行后立即获得机会参与当前的同步任务链。**

示例流程:
输出“我是第一个脚本” → 执行第一个write内部的alert → 输出“我是内部脚本” → 执行第二个write内部的alert → ...

第3步:混合写入引发的跨浏览器差异

当在同一个document.write块中既写了内联代码又写了外链src时,不同浏览器表现不一致:

  • IE9及以下版本:内联代码(无论是写在标签里还是用write写的)总是拥有最高优先级,先于所有write出来的外链脚本执行。所有的write外链脚本统一排在最末尾按顺序执行。
  • 其他现代浏览器:只有在第一个write出现之前的内联代码才会插队。凡是write出来的东西(不管内外),都要等整个主脚本块执行结束后,再按先后顺序排队执行。

为了消除这种兼容性问题并统一行为,最佳做法是将需要通过document.write注入的代码尽量集中,或者将纯内联的控制逻辑剥离出来单独放在后面的<script>标签中。

动态脚本与Ajax注入的非确定性

为了实现“无阻塞加载”以提升网页性能,开发者常采用动态创建<script>节点或利用XMLHttpRequest获取代码并注入DOM的手段。这种方式打破了传统的同步阻塞链条,但也牺牲了执行顺序的可控性。

第1步:动态添加外部脚本

通过JS创建一个<script>元素,设置其src属性并挂载到<head>中。

function loadScript(url, callback){
    var script = document.createElement("script");
    script.type = "text/javascript";
    if(script.readyState){
        script.onreadystatechange = function(){
            if(script.readyState === "loaded" || script.readyState === "complete"){
                callback && callback();
            }
        }
    }else{
        script.onload = function(){
            callback && callback();
        }
    }
    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

由于网络延迟、CDN分发速度等因素的不同,后添加的脚本未必比先添加的脚本先加载完成。因此,这种方法创建的脚本**绝对无法保证按添加顺序执行**。如果需要依赖特定的调用顺序,必须在回调函数中进行层层嵌套调用,或借助jQuery等库提供的工具方法处理队列。

第2步:Ajax注入脚本

利用XMLHttpRequest拉取JS源码,再通过内存操作赋值给新节点的text属性。此方法的执行顺序严格取决于传输模式:

  1. 同步请求 (async=false):脚本拉取期间页面会卡死,直到拿回代码执行。此时它与标准同步脚本无异,能够保证绝对的先后执行顺序。
  2. 异步请求 (async=true):属于典型的非阻塞操作。脚本的取得与执行时间是不确定的,因此其最终运行顺序也是无序的。

总结

JavaScript在浏览器中的运行秩序由“阻塞性”主导。标准的<script>标签如同铁律般从上到下执行,不受网速影响;而document.write更像是一个可变的操作台,根据写入内容的类型(内联或外链)及其所处上下文决定权重的分配;至于动态注入和异步请求,则是彻底放飞自我,不再服从任何既定的序列,需要辅以明确的工程化手段来控制。以上就是浏览器执行JavaScript脚本加载与代码执行顺序的详细内容,更多关于JavaScript运行机制的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。