第七章:JavaScript性能优化实战

server/2025/2/24 1:27:14/

JavaScript性能直接关乎网页交互体验,优化工作涵盖代码结构规整、执行效率提升以及内存管理等方面,对提升页面流畅度极为关键。

7.1 优化代码结构

7.1.1 函数节流与防抖

在处理频繁触发的事件时,函数节流(Throttle)和防抖(Debounce)能有效减少不必要的函数调用,提升性能。

  • 函数节流:设定一个固定的时间周期,在这个周期内,无论事件触发多少次,函数都只执行一次。

  • 错误示范:在窗口滚动事件中实时获取滚动位置并执行复杂计算,没有使用节流函数,导致性能问题。

window.addEventListener('scroll', () => {// 执行复杂计算,如获取滚动位置并实时更新大量DOM元素const scrollTop = window.pageYOffset;const elements = document.getElementsByTagName('div');for (let i = 0; i < elements.length; i++) {elements[i].style.transform = `translateY(${scrollTop}px)`;}
});
  • 正确示例:使用节流函数限制计算频率。
function throttle(func, limit) {let inThrottle;return function() {const args = arguments;const context = this;if (!inThrottle) {func.apply(context, args);inThrottle = true;setTimeout(() => inThrottle = false, limit);}};
}window.addEventListener('scroll', throttle(() => {const scrollTop = window.pageYOffset;const targetElement = document.getElementById('target - element');targetElement.style.transform = `translateY(${scrollTop}px)`;
}, 200));
  • 函数防抖:在事件触发后,等待一定时间,如果在这段时间内事件再次触发,则重新计时,直到等待时间结束后才执行函数。

  • 错误示范:在搜索框输入事件中,未使用防抖函数,导致用户每次输入都会立即发送搜索请求,造成网络资源浪费和性能问题。

<input type="text" id="search - input" oninput="search()">
<script>
function search() {const keyword = document.getElementById('search - input').value;// 发送搜索请求console.log('搜索关键词:', keyword);
}
</script>
  • 正确示例:使用防抖函数,用户输入结束后再执行搜索请求。
function debounce(func, delay) {let timer;return function() {const context = this;const args = arguments;clearTimeout(timer);timer = setTimeout(() => func.apply(context, args), delay);};
}const searchInput = document.getElementById('search - input');
searchInput.addEventListener('input', debounce(() => {const keyword = searchInput.value;// 发送搜索请求console.log('搜索关键词:', keyword);
}, 300));

7.1.2 模块化与代码拆分

将代码拆分成独立的模块,不仅提高代码的可维护性和复用性,还能优化加载性能。在大型项目中,使用ES6模块系统或构建工具(如Webpack)进行代码拆分。

  • ES6模块:通过 import 和 export 语句,将不同功能的代码封装在独立文件中。

  • 错误示范:将所有代码写在一个文件中,没有进行模块化,导致代码混乱,难以维护。

// 所有代码混合在一个文件中
function add(a, b) {return a + b;
}function subtract(a, b) {return a - b;
}// 其他大量功能代码- 正确示例:将工具函数封装在 utils.js 文件中,通过 import 和 export 进行模块化管理。// utils.js
export function add(a, b) {return a + b;
}export function subtract(a, b) {return a - b;
}// main.js
import { add, subtract } from './utils.js';
const result1 = add(1, 2);
const result2 = subtract(5, 3);
  • Webpack代码拆分:利用Webpack的 splitChunks 配置,可以将公共代码和异步加载的模块拆分出来,实现按需加载。

  • 错误示范:没有配置Webpack代码拆分,所有代码打包在一个文件中,导致初始加载文件过大。

// webpack.config.js,没有配置代码拆分
module.exports = {//...
};
  • 正确示例:配置 splitChunks 将公共代码和异步加载的模块拆分出来。
// webpack.config.js
module.exports = {//...optimization: {splitChunks: {chunks: 'all'}}
};

7.2 提升执行效率

7.2.1 避免全局变量

全局变量会增加命名空间冲突的风险,同时影响垃圾回收机制的效率。尽量将变量定义在函数内部或模块作用域内。

  • 错误示例:
let globalVar;
function init() {globalVar = 10;//...
}
  • 正确示例:
function init() {const localVar = 10;//...
}

7.2.2 优化循环操作

在循环中,减少不必要的计算和DOM操作,缓存循环长度,避免每次循环都进行计算。

  • 优化前:
const list = document.getElementById('my - list');
const items = list.getElementsByTagName('li');
for (let i = 0; i < items.length; i++) {items[i].style.color ='red';// 其他复杂计算,如每次循环都重新计算一个复杂的数学表达式const complexResult = Math.sqrt(i * i + 10) * Math.sin(i);console.log(complexResult);
}
  • 优化后:
const list = document.getElementById('my - list');
const items = list.getElementsByTagName('li');
const len = items.length;
for (let i = 0; i < len; i++) {items[i].style.color ='red';
}

7.3 内存管理

7.3.1 理解垃圾回收机制

JavaScript采用自动垃圾回收机制,通过标记清除(Mark - and - Sweep)算法回收不再使用的内存。当一个对象不再被任何变量引用时,它就会被标记为可回收,垃圾回收器会在适当的时候回收其占用的内存。

7.3.2 避免内存泄漏

内存泄漏是指不再使用的内存没有被及时回收,导致内存占用不断增加。闭包是导致内存泄漏的常见原因之一,下面着重介绍闭包相关的内存泄漏场景及解决办法。

  • 闭包导致内存泄漏的原理:闭包是指函数可以访问并操作其外部作用域的变量,即使外部函数已经执行完毕。如果闭包引用的外部变量在闭包外不再使用,但闭包仍然存在,会导致该变量无法被回收。
  • 错误示例1:事件监听器中的闭包内存泄漏
const element = document.getElementById('myElement');
function setupClickListener() {const largeData = new Array(1000000).fill(1);element.addEventListener('click', function clickHandler() {// clickHandler 形成闭包,largeData无法被回收console.log('Element clicked');});
}
setupClickListener();

在这个例子中, clickHandler 函数形成了闭包,它引用了 setupClickListener 函数作用域内的 largeData 。即使 setupClickListener 函数执行完毕, largeData 不再被外部使用,但由于 clickHandler 仍然存在(作为事件监听器), largeData 无法被垃圾回收,从而导致内存泄漏。

  • 错误示例2:返回函数形成的闭包内存泄漏
function outer() {const data = {value: new Array(500000).fill(42)};return function inner() {// inner 函数形成闭包,data无法被回收return data.value;};
}
const closureFunction = outer();
// 后续代码中,outer 函数作用域内的 data 不再被外部使用,但因闭包存在无法回收

这里 outer 函数返回的 inner 函数形成闭包,持续引用 data 对象。即使 outer 函数执行结束, data 在外部不再被使用,可因为 inner 函数的存在, data 不能被垃圾回收,造成内存泄漏。

  • 解决办法:

  • 及时移除事件监听器:在不需要事件监听器时,使用 removeEventListener 方法移除它,从而解除闭包对外部变量的引用。

const element = document.getElementById('myElement');
function setupClickListener() {const largeData = new Array(1000000).fill(1);const clickHandler = function() {console.log('Element clicked');};element.addEventListener('click', clickHandler);// 假设在某个条件下需要移除事件监听器setTimeout(() => {element.removeEventListener('click', clickHandler);// 此时 clickHandler 不再被引用,largeData 可以被回收}, 5000);
}
setupClickListener();
  • 手动解除闭包引用:如果闭包不再需要使用,可以将闭包赋值为 null ,强制解除对外部变量的引用。
function outer() {const data = {value: new Array(500000).fill(42)};return function inner() {return data.value;};
}
let closureFunction = outer();
// 使用闭包
closureFunction();
// 不再需要闭包时
closureFunction = null;
// 此时 data 可以被垃圾回收

7.3.3 如何查看内存泄漏

  • Chrome DevTools:

  • 使用“Memory”面板拍摄快照对比:打开开发者工具的“Memory”面板,先点击“Take snapshot”获取页面初始状态的内存快照。然后进行一系列操作,比如频繁触发可能导致内存泄漏的函数或事件。操作完成后,再次点击“Take snapshot”获取新的内存快照。在快照对比界面,可以查看对象的数量变化,若某些对象数量持续增加且无合理原因,可能存在内存泄漏。例如在上述闭包导致内存泄漏的代码示例中,多次触发点击事件后,对比快照会发现 largeData 相关对象没有减少,反而不断增多。

  • 使用“Record”功能记录内存变化:点击“Record”开始记录内存使用情况,在记录期间进行操作。完成操作后,点击“Stop”停止记录,此时会生成一个时间轴,展示内存使用随时间的变化趋势。如果在操作过程中内存持续上升且没有回落,可能存在内存泄漏。

  • Performance API:通过 performance.memory 属性可以获取内存相关信息,包括当前的堆内存使用量等。可以在代码中合适的位置插入获取内存信息的代码,比如在某个函数执行前后,对比内存使用量的变化。

console.log('函数执行前内存使用量:', performance.memory.usedJSHeapSize);
// 执行可能导致内存泄漏的函数
someFunctionThatMayLeakMemory();
console.log('函数执行后内存使用量:', performance.memory.usedJSHeapSize);

如果执行后内存使用量大幅增加且后续没有下降,就需要进一步排查是否存在内存泄漏。

  • Node.js环境下的排查工具:

  • 使用 heapdump 模块:安装 heapdump 模块后,在代码中合适位置插入 heapdump.writeSnapshot() 方法来生成堆内存快照文件。例如:

const heapdump = require('heapdump');
// 执行一些操作后生成快照
setTimeout(() => {heapdump.writeSnapshot();
}, 5000);

然后使用 node - heapdump 等工具分析生成的快照文件,查看对象的引用关系和内存占用情况,找出可能导致内存泄漏的对象。

  • 借助 process.memoryUsage() 方法:该方法可以获取当前Node.js进程的内存使用信息,包括RSS(resident set size,进程占用的物理内存大小)、heapTotal(V8堆的总大小)和heapUsed(V8堆中已使用的大小)等。可以在代码中定期调用该方法,观察内存使用趋势,判断是否有内存泄漏。
setInterval(() => {const memoryUsage = process.memoryUsage();console.log('RSS:', memoryUsage.rss);console.log('Heap Total:', memoryUsage.heapTotal);console.log('Heap Used:', memoryUsage.heapUsed);
}, 1000);

通过这些方法,可以有效避免闭包导致的内存泄漏,提升JavaScript代码的内存使用效率,进而优化整体性能。


http://www.ppmy.cn/server/170233.html

相关文章

android13修改系统Launcher不跟随重力感应旋转

android13系统中需要修改系统原生Launcher不跟随重力感应旋转。 通过代码查找发现packages/apps/Launcher3/src/com/android/launcher3/states/RotationHelper.java中存在一个函数getAllowRotationDefaultValue&#xff0c;用于获取是否允许旋转的默认值。 public static bo…

Obsidian·Copilot 插件配置(让AI根据Obsidian笔记内容进行对话)

安装&#xff1a; Obsidian的“第三方插件”搜索Copilot。 首先准备好API keys&#xff0c;使用硅基流动的API keys&#xff08;填写邀请码XDSDxSXR可彼此赠送2000万Tokens&#xff09; 配置&#xff1a; &#xff08;1&#xff09;Model选项卡&#xff08;配置Chat Model&…

一些时间方法

1.禁用之前的时间 <el-date-picker:picker-options"disableBeforePicker"disableBeforePicker: {disabledDate(time) {return time.getTime() < Date.now() - 8.64e7;}, }, 2.选择开始时间之后&#xff0c;结束时间为开始时间之后的120分钟&#xff0c;他们的…

BigData File Viewer报错

文章目录 背景回顾原因解析解决方法 背景回顾 博主在做项目是&#xff0c;需要查看parquet文件&#xff0c;考虑到没有现成的exe程序或在线解析网站&#xff0c;就找到了这个工具BigDataFileViewer github仓库地址 gitee仓库地址 但是在按教程步骤进行是发生了报错。报错信息如…

【JT/T 808协议】808 协议开发笔记 ② ( 终端注册 | 终端注册应答 | 字符编码转换网站 )

文章目录 一、消息头 数据1、消息头拼接2、消息 ID 字段3、消息体属性 字段4、终端手机号 字段5、终端流水号 字段 二、消息体 数据三、校验码计算四、最终计算结果五、终端注册应答1、分解终端应答数据2、终端应答 消息体 数据 六、字符编码转换网站 一、消息头 数据 1、消息头…

Windows 上编译 mebedtls 的鸿蒙库

mebedtls 地址&#xff1a;https://github.com/Mbed-TLS/mbedtls 准备工作&#xff1a; clone mebedtls 仓库到本地(tag: mbedtls-2.26.0)鸿蒙工具链(SDK version: v5.0.5) 编译文件修改&#xff1a; 对 CMakeLists.txt 进行修改&#xff0c;主要是关闭了以下几个选项 ENABLE_P…

鸿蒙系统-同应用跨设备数据同步(分布式功能)

一. 场景介绍 跨设备数据同步功能&#xff08;即分布式功能&#xff09;&#xff0c;指将数据同步到一个组网环境中的其他设备。常用于用户应用程序数据内容在可信认证的不同设备间&#xff0c;进行自由同步、修改和查询。 二. 跨设备同步访问控制机制 在进行开发之前&#…

黑客利用 Telegram API 传播新的 Golang 后门

Netskope 的网络安全研究人员发现了一种新型的、具备一定功能但可能仍处于开发阶段的基于 Golang 语言编写的后门程序&#xff0c;该程序利用 Telegram 进行指令与控制&#xff08;C2&#xff09;活动。这种恶意软件&#xff08;Trojan.Generic.37477095&#xff09;疑似源自俄…