解锁JavaScript性能优化:从理论到实战

文章目录

  • 前言
  • 一、常见性能瓶颈剖析
  • 二、实战案例与优化方案
    • (一)DOM 操作优化案例​
    • (二)事件绑定优化案例​
    • (三)循环与递归优化案例​
    • (四)内存管理优化案例​
  • 三、性能优化工具介绍
  • 总结


前言

性能优化的重要性
在这里插入图片描述
在当今数字化时代,Web 应用已成为人们生活和工作中不可或缺的一部分。而 JavaScript 作为 Web 开发的核心语言,其性能优劣直接决定了用户体验的好坏。想象一下,当你满心期待地打开一个网页,却遭遇长时间的加载等待,或者在操作过程中页面频繁卡顿,这种糟糕的体验无疑会让你对该应用失去耐心和好感。​
对于企业来说,性能问题可能导致用户流失、业务受损。根据相关研究表明,网页加载时间每增加一秒,用户流失率可能会上升 7% ,转化率也会大幅下降。而在搜索引擎排名方面,性能出色的网站往往更受青睐,能获得更高的权重和曝光机会。此外,良好的性能优化还能降低服务器负载,节约运营成本。​
由此可见,JavaScript 性能优化绝非可有可无,而是关乎应用成败的关键因素。接下来,本文将通过一系列真实的实战案例,深入剖析性能瓶颈产生的原因,并详细介绍针对性的优化方法,助你掌握提升 JavaScript 性能的核心技巧 。


一、常见性能瓶颈剖析

(一)重绘与重排​
在网页渲染过程中,重绘(Repaint)和重排(Reflow,也称为回流)是两个重要概念,它们直接影响着 JavaScript 的性能。当元素的样式改变但不影响其在文档流中的位置和几何形状时,比如改变元素的颜色、背景、边框等,浏览器会进行重绘,这个过程只需要重新绘制受影响的元素,无需重新计算布局 ,代价相对较小。然而,当元素的尺寸、布局或位置发生改变,如改变元素的宽度、高度、添加 / 删除元素、改变窗口大小等操作时,浏览器需要重新计算文档中元素的位置和大小,此为重排。重排是一个代价较高的操作,因为它不仅会影响当前元素及其子元素,甚至可能影响后续兄弟元素和祖先元素,浏览器需要重新计算布局,并重新绘制受影响的部分。​
频繁的 DOM 操作是引发重绘和重排的常见原因。以一个简单的列表为例,如果通过循环逐个修改列表项的样式,每一次修改都可能触发重排和重绘。如下面的代码:

const listItems = document.querySelectorAll('li');
for (let i = 0; i < listItems.length; i++) {listItems[i].style.color = 'red';listItems[i].style.marginLeft = '10px';
}

在这个例子中,每次循环都对列表项的样式进行了两次修改,这会导致浏览器频繁地进行重排和重绘,严重影响性能。​
(二)主线程阻塞​
JavaScript 是单线程语言,这意味着它在同一时间只能执行一个任务。在浏览器环境中,JavaScript 的执行与页面渲染、用户交互等都在主线程中进行。当主线程被一些复杂的计算任务或者大量的 DOM 操作占据时,就会导致主线程阻塞。例如,进行复杂的数学运算、解析庞大的 JSON 数据、频繁地操作 DOM 元素等。​
假设我们有一个计算斐波那契数列的函数:

function fibonacci(n) {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2);
}// 进行大量的斐波那契数列计算,阻塞主线程
for (let i = 0; i < 40; i++) {fibonacci(i);
}

在上述代码中,fibonacci函数是一个递归函数,计算过程非常耗时。当在循环中多次调用该函数时,会使主线程长时间处于忙碌状态,导致页面无法及时响应用户的操作,如点击按钮、滚动页面等,给用户带来极差的体验。​
(三)内存泄漏​
内存泄漏指的是程序中已分配的内存由于某种原因无法被释放,导致内存占用不断增加,最终可能影响程序的性能甚至导致程序崩溃。在 JavaScript 中,常见的内存泄漏场景有闭包滥用、未移除事件监听器等。​
当闭包被不当使用时,就可能引发内存泄漏。例如:

function outerFunction() {const largeData = new Array(1000000);  // 占用大量内存的数据return function innerFunction() {console.log(largeData.length);};
}const closure = outerFunction();  // 这里形成闭包,即使outerFunction执行完毕,largeData也无法被回收

在这个例子中,outerFunction返回的innerFunction形成了闭包,它引用了outerFunction中的largeData。即使outerFunction执行完毕,largeData仍然被innerFunction引用,从而无法被垃圾回收机制回收,造成内存泄漏。​
另外,在为 DOM 元素添加事件监听器后,如果在元素被移除时没有手动移除对应的事件监听器,也会导致内存泄漏。例如:

const button = document.createElement('button');
button.addEventListener('click', function clickHandler() {console.log('Button clicked');
});
document.body.appendChild(button);// 后续移除按钮,但未移除事件监听器
document.body.removeChild(button);

在上述代码中,按钮被从页面移除后,其点击事件监听器仍然存在于内存中,并且持有对按钮 DOM 元素的引用,导致按钮及其相关资源无法被回收,造成内存泄漏。

二、实战案例与优化方案

(一)DOM 操作优化案例​

在 Web 开发中,动态渲染长列表是一个常见的需求。但如果处理不当,就会引发严重的性能问题。假设我们要在页面上渲染一个包含 10000 条数据的列表,以下是一段可能的低效代码:

const data = Array.from({ length: 10000 }, (_, i) => `Item ${i}`);
const list = document.getElementById('list');
data.forEach(item => {list.innerHTML += `<li>${item}</li>`;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95779.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95779.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结构化记忆、知识图谱与动态遗忘机制在医疗AI中的应用探析(上)

往期相关内容推荐: 基于Python的多元医疗知识图谱构建与应用研究(上)

XSS攻击:从原理入门到实战精通详解

一、XSS攻击基础概念1.1 什么是XSS攻击 XSS&#xff08;Cross-Site Scripting&#xff0c;跨站脚本攻击&#xff09;是一种将恶意脚本注入到可信网站中的攻击手段。当用户访问被注入恶意代码的页面时&#xff0c;浏览器会执行这些代码&#xff0c;导致&#xff1a;用户会话被劫…

Leetcode 14 java

今天复习一下以前做过的题目&#xff0c;感觉是忘光了。 160. 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数…

用 FreeMarker 动态构造 SQL 实现数据透视分析

在 ERP、BI 等系统中&#xff0c;数据透视分析&#xff08;Pivot Analysis&#xff09;是非常常见的需求&#xff1a;用户希望按任意维度&#xff08;如门店、时间、商品分类等&#xff09;进行分组统计&#xff0c;同时选择不同的指标&#xff08;如 GMV、订单数、客单价等&am…

13.深度学习——Minst手写数字识别

第一部分——起手式 import torch from torchvision import datasets, transforms import torch.nn as nn import torch.nn.functional as F import torch.optim as optimuse_cuda torch.cuda.is_available()if use_cuda:device torch.device("cuda") else: device…

【JAVA高级】实现word转pdf 实现,源码概述。深坑总结

之前的需求做好后,需求,客户突发奇想。要将生成的word转为pdf! 因为不想让下载文档的人改动文档。 【JAVA】实现word添加标签实现系统自动填入字段-CSDN博客 事实上这个需求难度较高,并不是直接转换就行的 word文档当中的很多东西都需要处理 public static byte[] gener…

数据驱动测试提升自动化效率

测试工程师老王盯着满屏重复代码叹气&#xff1a;“改个搜索条件要重写20个脚本&#xff0c;这班加到啥时候是个头&#xff1f;” 隔壁组的小李探过头&#xff1a;“试试数据驱动呗&#xff0c;一套脚本吃遍所有数据&#xff0c;我们组上周测了300个组合都没加班&#xff01;”…

模板引用(Template Refs)全解析2

三、v-for 中的模板引用 当在 v-for 中使用模板引用时,引用的 value 会自动变为一个数组,包含列表中所有元素/组件的引用(需 Vue 3.5+ 版本,旧版需手动处理且顺序不保证)。 1. 基本用法(Vue 3.5+) <script setup> import { ref, useTemplateRef, onMounted } f…

【Linux系统】进程间通信:System V IPC——共享内存

前文中我们介绍了管道——匿名管道和命名管道来实现进程间通信&#xff0c;在介绍怎么进行通信时&#xff0c;我们有提到过不止管道的方式进行通信&#xff0c;还有System V IPC&#xff0c;今天这篇文章我们就来学习一下System V IPC中的共享内存1. 为何引入共享内存&#xff…

[优选算法专题二滑动窗口——最大连续1的个数 III]

题目链接 最大连续1的个数 III 题目描述 题目解析 问题本质 输入&#xff1a;二进制数组nums&#xff08;只包含 0 和 1&#xff09;和整数k操作&#xff1a;最多可以将k个 0 翻转成 1目标&#xff1a;找到翻转后能得到的最长连续 1 的子数组长度 这个问题的核心是要找到一…

C#单元测试(xUnit + Moq + coverlet.collector)

C#单元测试 xUnit Moq coverlet.collector 1.添加库 MlyMathLib 2.编写库函数内容 using System;namespace MlyMathLib {public interface IUserRepo{string GetName(int id);}public class UserService{private readonly IUserRepo _repo;public UserService(IUserRepo repo…

【数据库】Oracle学习笔记整理之五:ORACLE体系结构 - 参数文件与控制文件(Parameter Files Control Files)

Oracle体系结构 - 参数文件与控制文件&#xff08;Parameter Files & Control Files&#xff09; 参数文件与控制文件是Oracle数据库的“双核基石”&#xff1a;参数文件是实例的“启动配置中心”&#xff0c;定义运行环境与规则&#xff1b;控制文件是数据库的“物理元数据…

GDB典型开发场景深度解析

GDB典型开发场景深度解析 以下是开发过程中最常见的GDB使用场景&#xff0c;结合具体实例和调试技巧&#xff0c;帮助开发者高效解决实际问题&#xff1a;一、崩溃分析&#xff08;Core Dump调试&#xff09; 场景&#xff1a;程序突然崩溃&#xff0c;生成了core文件 # 启动调…

存储、硬盘、文件系统、 IO相关常识总结

目录 &#xff08;一&#xff09;存储 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;分类 &#xff08;二&#xff09;硬盘 &#xff08;1&#xff09;容量&#xff08;最主要的参数&#xff09; &#xff08;2&#xff09;转速 &#xff08;3&#xff09;访…

docker安装mongodb及java连接实战

1.docker部署mongodb docker run --name mongodb -d -p 27017:27017 -v /data/mongodbdata:/data/db -e MONGO_INITDB_ROOT_USERNAMEtestmongo -e MONGO_INITDB_ROOT_PASSWORDtest123456 mongodb:4.0.112.项目实战 <dependencies><dependency><groupId>org.m…

Java设计模式之《工厂模式》

目录 1、介绍 1.1、定义 1.2、优缺点 1.3、使用场景 2、实现 2.1、简单工厂模式 2.2、工厂方法模式 2.3、抽象工厂模式 3、小结 前言 在面向对象编程中&#xff0c;创建对象实例最常用的方式就是通过 new 操作符构造一个对象实例&#xff0c;但在某些情况下&#xff0…

【异步】js中异步的实现方式 async await /Promise / Generator

JS的异步相关知识 js里面一共有以下异步的解决方案 传统的回调 省略 。。。。 生成器 Generator 函数是 ES6 提供的一种异步编程解决方案, 语法上&#xff0c;首先可以把它理解成&#xff0c;Generator 函数是一个状态机&#xff0c;封装了多个内部状态。执行 Generator 函数…

JVM字节码文件结构

Class文件结构class文件是二进制文件&#xff0c;这里要介绍的是这个二级制文件的结构。思考&#xff1a;一个java文件编译成class文件&#xff0c;如果要描述一个java文件&#xff0c;需要哪些信息呢&#xff1f;基本信息&#xff1a;类名、父类、实现哪些接口、方法个数、每个…

11.web api 2

5. 操作元素属性 5.1操作元素常用属性 &#xff1a;通过 JS 设置/修改标签元素属性&#xff0c;比如通过 src更换 图片最常见的属性比如&#xff1a; href、title、src 等5.2 操作元素样式属性 &#xff1a;通过 JS 设置/修改标签元素的样式属性。使用 className 有什么好处&a…

java中数组和list的区别是什么?

在Java中&#xff0c;数组&#xff08;Array&#xff09;和List&#xff08;通常指java.util.List接口的实现类&#xff0c;如ArrayList、LinkedList&#xff09;是两种常用的容器&#xff0c;但它们在设计、功能和使用场景上有显著区别。以下从核心特性、使用方式等方面详细对…