JavaScript 性能优化实战:从加载到执行的全链路指南
本文系统梳理了 JavaScript 性能优化的核心策略,涵盖脚本加载位置与合并、DOM 操作与重排重绘控制、变量作用域对访问速度的影响以及循环与二进制运算的优化技巧。通过具体代码对比,帮助开发者提升页面渲染效率与执行性能。
JavaScript 脚本加载策略
一、关于 JavaScript 文件的加载位置
在 HTML 文档中,<script> 标签可置于 <head> 或 <body> 区域。鉴于 JavaScript 执行与 UI 渲染共享单线程,若 JS 文件加载会阻塞后续页面解析,浏览器需等待 JS 完全加载并执行完毕后才继续后续操作。此机制可能导致页面出现空白或卡顿。前端开发不仅需实现功能,更应关注优质用户体验。为消除用户等待的枯燥感,针对此问题提出两种解决方案:
1. 若 JS 文件无特殊要求在页面渲染前必须载入及编译完成,建议将 JS 文件置于 </body> 标签之前(即所有页面呈现内容之后),而 CSS 文件仍保留在 <head> 区域(避免用户看到布局混乱的页面)。此举可让用户先看到有布局的页面而非空白页。针对数据需通过 JS 请求加载的情况,可对数据加载顺序进行排序:急需呈现的接口优先执行,非关键接口延后执行,并配合简单的载入动画或提示。
2. 若 JS 文件指定需先执行以更好展示页面内容,可在首个 JS 或页面初始阶段添加载入小动画,如有趣或可爱的动画场景。此举能有效缓解用户等待时的无聊感,甚至可能因动画趣味性提升项目用户体验。
最终推荐:将 <script> 标签尽可能放置在 </body> 标签之前加载,以优化用户体验。
二、关于 JS 文件的合并 在团队开发中,通常将不同功能的代码块分别置于不同的 JS 文件中,以便多人协作开发时更易定位对应文件夹或文件,而非在长文件中查找方法。这确实能提高团队开发效率及新人二次开发和维护的便利性。然而,从页面性能角度审视,这正是问题所在。书中指出:Each HTTP request brings with it additional performance overhead,so downloading one single 100 KB file will be faster than downloading four 25 KB files.
下载 1 个 100KB 的文件比下载 4 个 25KB 的文件更快,而开发过程中区分各个文件又有诸多好处,因此合并操作通常安排在开发完成后进行。相信此操作大家并不陌生,当前前端工具丰富,可根据习惯选择相应的压缩工具。 此处简要提及,在文件加载方面还可利用 defer 和 async 属性,分别用于延迟加载和异步加载。现代浏览器大多已支持 defer 属性,若尚未习惯使用且不确定是否存在潜在问题,有兴趣者可自行 Google 相关知识点,此处仅作简要提及。
当前主流框架大多已配合懒加载和按需加载机制。
三、实现更快速的数据访问
对于浏览器而言,标识符所处的作用域层级越深,读写该标识符的速度越慢(原型链查找机制亦遵循此规律)。这一点易于理解,简单比喻为:杂货店距离家越远,购买酱油所花费的时间就越长…… 熊孩子,打酱油耗时过长,菜早已烧焦 -.-~
若需在当前函数内多次使用某变量值,可先将其存储于局部变量中,案例如下:
//修改前
function showLi(){
var i = 0;
for(;i<document.getElementsByTagName("li").length;i++){ //一次访问document
console.log(i,document.getElementsByTagName("li")[i]); //三次访问document
};
};
//修改后
function showLi(){
var li_s = document.getElementsByTagName("li"); //一次访问document
var i = 0;
for(;i<li_s.length;i++){
console.log(i,li_s[i]); //三次访问局部变量li_s
};
};
DOM 操作与渲染优化
四、DOM 操作的优化
众所周知,DOM 操作比 JavaScript 代码执行更消耗性能。尽管无法完全避免 DOM 操作,但可尽量降低其对性能的消耗。
通过以下代码示例阐释该问题:
function innerLi_s(){
var i = 0;
for(;i<20;i++){
document.getElementById("Num").innerHTML="A"; //进行了20次循环,每次又有2次DOM元素访问:一次读取innerHTML的值,一次写入值
};
};
针对上述方法进行的改写示例:
function innerLi_s(){
var content ="";
var i = 0;
for(;i<20;i++){
content += "A"; //这里只对js的变量循环了20次
};
document.getElementById("Num").innerHTML += content; //这里值进行了一次DOM操作,又分2次DOM访问:一次读取innerHTML的值,一次写入值
};
五、减少 DOM 的重绘与重排版
元素布局改变、内容增删改或浏览器窗口尺寸变化均会导致重排(Reflow);而字体颜色或背景色修改则导致重绘(Repaint)。 对于类似以下代码的操作,据称现代浏览器大多已进行优化(将其优化为仅执行 1 次重排版):
//修改前
var el = document.getElementById("div");
el.style.borderLeft = "1px"; //1次重排版
el.style.borderRight = "2px"; //又1次重排版
el.style.padding = "5px"; //还有1次重排版
//修改后
var el = document.getElementById("div");
el.style.cssText = "border-left:1px;border-right:2px;padding:5px"; //1次重排版
针对多重操作,以下三种方法亦可减少重排版和重绘的次数:
1. DOM 先隐藏,操作后再显示:涉及 2 次重排(临时设置 display:none)
2. 使用 document.createDocumentFragment() 创建文档片段进行处理,操作完成后追加至页面:涉及 1 次重排
3. var newDOM = oldDOM.cloneNode(true) 创建 DOM 副本,修改副本后通过 oldDOM.parentNode.replaceChild(newDOM,oldDOM) 覆盖原 DOM:涉及 2 次重排
循环与底层运算优化
六、循环的优化
这应是较多开发者已熟知的写法,此处简要带过(后续仍将以代码加注释形式说明)~
//修改前
var i = 0;
for(;i<arr.lengthli++){ //每次循环都需要获取数组arr的length
console.log(arr[i]);
}
//修改后
var i = 0;
var len = arr.length; //获取一次数组arr的length
for(;i<len;i++){
console.log(arr[i]);
}
//or
var i = arr.length;;
for(;i;i--){
console.log(arr[i]);
}
七、合理利用二进制操作
例如:对 2 取模,偶数最低位为 0,奇数最低位为 1。与 1 进行位与(AND)操作时,结果为 0 表示偶数,结果为 1 表示奇数。
代码如下:
.odd{color:red}
.even{color:yellow}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
var i = 0;
var lis = document.getElementsByTagName("li");
var len = lis.length;
for(;i<len;i++){
if(i&1){
lis[i].className = "even";
} else{
lis[i].className = "odd";
}
};
尽管现代浏览器性能已大幅提升,但作者认为这是提升代码质量的一种追求。可能单个或少数几个优化点不会带来显著的性能影响,但从多个维度进行优化后,可能产生质的飞跃。
以上总结的 JavaScript 提高性能知识点,希望大家能够熟练掌握。
配置 Matter.js 睡眠机制以优化静止物体性能
2026-11-05 15:43:12
JavaScript console.log高级用法:用table、group和time优化调试输出
2026-11-05 08:04:56
JS变量中存储HTML代码时的特殊字符转义方法
2026-11-04 19:04:09
JS怎么安全转换带千分位或货币符号的字符串为数字
2026-11-04 11:54:14
推荐几款稳定的在线JS反混淆工具,支持Obfuscator.io解密
2026-11-04 08:16:21
