ECMAScript性能优化技巧与陷阱
大家好,我是程序员小羊!
前言
ECMAScript,即JavaScript,是一种广泛应用于Web开发中的脚本语言。随着现代Web应用的复杂度日益增加,如何优化JavaScript的性能变得至关重要。性能优化不仅能提高应用的响应速度,还能降低资源消耗,改善用户体验。然而,在优化过程中,开发者常常会遇到一些陷阱,导致性能不升反降。本文将详细探讨ECMAScript性能优化的技巧与常见陷阱,帮助开发者更好地编写高效的JavaScript代码。
一、性能优化的核心原则
在讨论具体的优化技巧之前,了解一些基本的性能优化原则是非常重要的:
- 减少不必要的计算:避免重复计算、无用计算,尽量将计算放在必要时刻进行。
- 降低内存消耗:通过合理管理对象的生命周期、避免内存泄漏,减少不必要的内存占用。
- 避免阻塞主线程:JavaScript在浏览器中是单线程执行的,任何耗时操作都会阻塞UI的渲染和用户交互。
- 利用现代特性与工具:使用现代ECMAScript特性和工具,可以让代码更简洁、性能更优。
二、ECMAScript性能优化技巧
2.1 避免全局查找与变量提升
在JavaScript中,访问全局变量比访问局部变量更耗时,因为JavaScript引擎需要沿着作用域链查找变量。为了提高性能,应该尽量减少全局变量的使用,并将频繁使用的全局变量缓存为局部变量。
// 缓存全局变量
const document = window.document;
const elem = document.getElementById('myElement');
此外,尽量避免使用变量提升(Hoisting)带来的性能损失。虽然JavaScript允许在声明之前使用变量,但这样会影响代码的可读性和执行效率。始终在使用变量之前声明变量,避免变量提升引发的问题。
// 错误示范
console.log(a); // undefined
var a = 10;// 优化示范
let a = 10;
console.log(a); // 10
2.2 使用事件委托
事件委托是处理大量DOM元素事件时的常见优化技巧。与其为每个元素都绑定事件,不如将事件绑定在其父元素上,通过事件冒泡机制统一处理子元素的事件。这不仅减少了内存消耗,还提高了性能。
// 示例:使用事件委托处理列表项点击事件
document.getElementById('list').addEventListener('click', function(event) {if (event.target && event.target.nodeName === 'LI') {console.log('List item clicked:', event.target.textContent);}
});
2.3 避免频繁的DOM操作
DOM操作是JavaScript中最耗时的操作之一。频繁的DOM操作会导致页面重排(reflow)和重绘(repaint),从而影响性能。可以通过以下方式优化DOM操作:
- 批量更新DOM:通过一次性插入或修改多个节点,减少DOM的重排和重绘次数。
- 使用文档片段(Document Fragment):在内存中创建文档片段进行DOM操作,最后一次性插入DOM树。
// 错误示范:频繁更新DOM
for (let i = 0; i < 1000; i++) {let item = document.createElement('li');item.textContent = `Item ${i}`;document.getElementById('list').appendChild(item);
}// 优化示范:使用文档片段
let fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {let item = document.createElement('li');item.textContent = `Item ${i}`;fragment.appendChild(item);
}
document.getElementById('list').appendChild(fragment);
2.4 合理使用setTimeout
与setInterval
在Web开发中,setTimeout
和setInterval
是常用的定时器函数。滥用这些函数会导致性能问题,尤其是在间隔时间设置得过短的情况下。应尽量避免使用过短的定时器间隔,并考虑使用requestAnimationFrame
来处理与动画相关的任务。
// 错误示范:过短的setInterval
setInterval(() => {console.log('This can cause performance issues');
}, 1); // 1ms间隔,极可能导致主线程阻塞// 优化示范:使用requestAnimationFrame
function animate() {// 动画逻辑console.log('Animating...');requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
2.5 谨慎使用闭包
闭包在JavaScript中是一个强大的工具,但使用不当会导致内存泄漏,尤其是在长时间运行的Web应用中。闭包会使得函数内部的变量不会被垃圾回收机制释放,除非手动清理。因此,在使用闭包时,确保不会无意中持有不必要的变量。
// 错误示范:未清理的闭包
function createClosure() {let largeArray = new Array(1000000).fill('data');return function() {console.log(largeArray[0]);};
}
let closure = createClosure();
// largeArray始终存在于内存中// 优化示范:手动清理
function createClosure() {let largeArray = new Array(1000000).fill('data');return function() {console.log(largeArray[0]);largeArray = null; // 手动清理};
}
let closure = createClosure();
2.6 减少内存泄漏
内存泄漏是影响JavaScript性能的重要问题,尤其是在单页应用(SPA)中。以下是常见的内存泄漏原因及其解决方案:
- 未解除的事件监听器:确保在不再需要时移除事件监听器。
- DOM节点的循环引用:避免DOM节点与JavaScript对象之间的循环引用,必要时手动断开引用。
- 定时器未清理:在组件卸载或页面离开时清理不再需要的定时器。
// 错误示范:未清理的事件监听器
function attachListener() {window.addEventListener('resize', function() {console.log('Window resized');});
}
// 在不再需要时,事件监听器仍然存在// 优化示范:清理事件监听器
function attachListener() {function onResize() {console.log('Window resized');}window.addEventListener('resize', onResize);return function() {window.removeEventListener('resize', onResize);};
}
let detach = attachListener();
// 在不再需要时调用detach以清理监听器
2.7 使用高效的循环结构
在JavaScript中,循环结构对性能有显著影响。优化循环的执行效率可以提高整体代码性能:
- 使用
for
循环代替forEach
:传统的for
循环在性能上通常优于forEach
和其他高级循环方法,尤其是在需要遍历大数组时。 - 减少循环中的计算:将循环中的不变计算移出循环体,避免不必要的重复计算。
// 错误示范:使用forEach和循环内计算
const data = [1, 2, 3, 4, 5];
data.forEach((item, index) => {console.log(item * index * Math.random());
});// 优化示范:使用for循环并移出不变计算
const data = [1, 2, 3, 4, 5];
const random = Math.random();
for (let i = 0; i < data.length; i++) {console.log(data[i] * i * random);
}
2.8 使用惰性加载与代码拆分
现代Web应用通常需要加载大量的JavaScript代码,这会影响页面的加载速度和初次渲染性能。通过惰性加载(Lazy Loading)和代码拆分(Code Splitting),可以减少初始加载时间,提高应用的性能。
- 惰性加载:只在需要时加载特定的代码或模块,避免初次加载过多的资源。
- 代码拆分:使用Webpack等工具将代码分割成多个块(chunks),按需加载,减少初始加载体积。
// 示例:动态导入模块(惰性加载)
function loadComponent() {import('./MyComponent.js').then(module => {const MyComponent = module.default;// 使用MyComponent});
}
三、常见的性能陷阱
3.1 未优化的递归
递归是解决某些问题的有效方法,但在JavaScript中递归使用不当会导致性能问题,甚至导致栈溢出。应尽量使用尾递归优化(如果引擎支持)或转化为迭代。
// 错误示范:未优化的递归
function factorial(n) {if (n <= 1) return 1;return n * factorial(n - 1);
}// 优化示范:使用尾递归(如支持)或迭代
function factorial(n, acc = 1) {if (n <= 1) return acc;return factorial(n - 1, n * acc);
}
// 或者使用迭代
function factorialIterative(n) {let result = 1;for (let i = 2; i <= n; i++) {result *= i;}return result;
}
3.2 误用eval
与with
eval
和with
是JavaScript中的两大性能陷阱。eval
允许执行动态代码,但它会强制JavaScript引擎取消一些优化,导致性能下降。with
则会引入新的作用域,增加变量解析的复杂性,同样影响性能。应尽量避免使用这两个语法结构。
// 错误示范:使用eval
const code = 'console.log("Hello World")';
eval(code); // 不仅影响性能,还存在安全风险// 优化示范:避免使用eval
const code = () => console.log("Hello World");
code();
3.3 无效的去抖与节流
去抖(Debounce)和节流(Throttle)是优化频繁事件触发(如滚动、输入)的常用技术。但不正确的实现或配置会导致优化无效,甚至引发性能问题。确保在实现去抖或节流时合理设定时间间隔,并根据实际需求调整策略。
// 错误示范:不合理的去抖实现
function debounce(fn, delay) {let timer;return function() {clearTimeout(timer);timer = setTimeout(fn, delay);};
}
window.addEventListener('resize', debounce(() => {console.log('Resized');
}, 1000)); // 间隔过长,影响体验// 优化示范:合理的去抖实现
function debounce(fn, delay) {let timer;return function() {clearTimeout(timer);timer = setTimeout(fn, delay);};
}
window.addEventListener('resize', debounce(() => {console.log('Resized');
}, 100)); // 合理的时间间隔
结尾
ECMAScript的性能优化需要开发者在代码编写、结构设计和工具使用等各方面做出合理选择。通过减少不必要的计算和内存消耗、避免阻塞主线程、利用现代ECMAScript特性以及合理使用工具和框架,可以大幅提升JavaScript应用的性能。同时,开发者还应当小心避免一些常见的性能陷阱,如滥用
eval
、不当的递归、以及无效的去抖和节流。在不断实践和优化的过程中,开发者可以逐步掌握提升JavaScript性能的关键技巧,打造出高效、流畅的Web应用。
今天这篇文章就到这里了,大厦之成,非一木之材也;大海之阔,非一流之归也。感谢大家观看本文
相关文章:

ECMAScript性能优化技巧与陷阱
大家好,我是程序员小羊! 前言 ECMAScript,即JavaScript,是一种广泛应用于Web开发中的脚本语言。随着现代Web应用的复杂度日益增加,如何优化JavaScript的性能变得至关重要。性能优化不仅能提高应用的响应速度&#x…...

c++实现B树(上)
哈喽啊!好久不见,甚是想念!失踪人口要回归了,时隔一个多月小吉我终于要更新blog了🎉。在停更的一个多月中,小吉也有在好好学习提升自己,立志给大家呈现好文章。 现在让我们进入正题吧…...

【机器学习】深度强化学习–RL的基本概念、经典场景以及算法分类
引言 深度强化学习(Deep Reinforcement Learning, DRL)是机器学习的一个分支,它结合了深度学习(Deep Learning)和强化学习(Reinforcement Learning, RL)的技术 文章目录 引言一、深度强化学习–…...

【git】将本地文件上传到github
安装git 选择一个文件夹作为git仓库,cd到文件夹输入 git init文件夹出现.git文件夹,该文件夹默认为隐藏文件夹,设置为不隐藏 在cmd中输入 ssh-keygen -t rsa -C "xxxxxx.com"该邮箱为github邮箱,然后一路enter出现以…...

安卓应用开发学习:手机摇一摇功能应用尝试--摇骰子和摇红包
一、引言 前几天,我发布的日志《安卓应用开发学习:查看手机传感器信息》记录了如何查看手机传感器的信息,通过上述的方法,可以看到我的OPPO手机支持19种传感器。本篇日志就记录一下常见的加速度传感器的典型应用——“摇一摇”功…...

HTML中的<fieldset>标签元素框的使用
HTML 提供的 <fieldset> 标签用于在表单中分组相关元素。 <fieldset> 标签会在相关元素周围绘制一个框。 <legend> 标签为 fieldset 元素定义标题。 语法如下: <fieldset><legend>标题</legend><!-- 元素内容... -->…...

Linux驱动入门实验班——SR501红外模块驱动(附百问网视频链接)
目录 一、工作方式 二、接口图 三、编写思路 1.构造file_operations结构体 2.实现read函数 3.编写入口函数 4.编写中断处理函数 5.编写出口函数 6.声明出入口函数以及协议 四、源码 五、课程链接 一、工作方式 SR501人体红外感应模块有两种工作模式: …...
windows C++- Com技术简介(上)
在介绍C和winrt与COM组件技术的关系之前,有必要介绍一下com组件技术,这项技术比较古老,但是它一直作为windows的基石存在。COM 是一类独立于平台且面向对象的分布式系统,用于创建可交互的二进制软件组件。 COM 技术是 Microsoft O…...

Jenkins持续集成工具学习
一、从装修厨房看项目开发效率优化 二、持续集成工具 三、JavaEE项目部署方式对比 四、JenkinsSVN持续集成环境搭建 五、JenkinsGitHub持续集成环境搭建...
Redis:查询是否包含某个字符/字符串之三
上一篇:Redis:查询是否包含某个字符/字符串之二-CSDN博客 摘要: 遍历key,在跟进value的类型遍历value是否包含指定字符串 search_strings ,这里使用redis-py库,默认只能处理utf-8编码,如果存在…...

【Redis】数据类型详解及其应用场景
目录 Redis 常⻅数据类型预备知识基本全局命令小结 数据结构和内部编码单线程架构引出单线程模型为什么单线程还能这么快 Redis 常⻅数据类型 Redis 提供了 5 种数据结构,理解每种数据结构的特点对于 Redis 开发运维⾮常重要,同时掌握每种数据结构的常⻅…...

PARA-Drive:设计并行模型实现端到端自动驾驶
论文链接 https://openaccess.thecvf.com/content/CVPR2024/papers/Weng_PARA-Drive_Parallelized_Architecture_for_Real-time_Autonomous_Driving_CVPR_2024_paper.pdfhttps://openaccess.thecvf.com/content/CVPR2024/papers/Weng_PARA-Drive_Parallelized_Architecture_fo…...
vs2022 x64 C/C++和汇编混编 遇到的坑
vs2022 x64 C/C和汇编混编 遇到的坑 遇到的问题二、问题复现1.出错代码2.问题分析2.1 堆栈对齐问题 3.解决方案 总结奇数和偶数个寄存器的影响为什么 sub rsp, 8 对奇数个寄存器有用?结论 遇到的问题 0x00007FFFFAE24A29 (msvcp140.dll)处(位于 TestCompileConsole…...

PHP概述、环境搭建与基本语法讲解
目录 【学习目标、重难点知识】 什么是网站? 1. PHP 介绍 1.1. PHP 概述 1.1.1. PHP 是什么? 1.1.2. PHP 都能做什么? 1.2. PHP 环境搭建 1.2.1. PhpStudy 2. PHP 基本语法 2.1. PHP 语法入门 2.1.1. 第一个 PHP 程序 2.1.2. PHP …...

实现信创Linux麦克风摄像头录制(源码,银河麒麟、统信UOS)
随着信创国产化浪潮的来临,在国产操作系统上的应用开发的需求越来越多,其中一个就是需要在银河麒麟或统信UOS上实现录制摄像头视频和麦克风声音,将它们录制成一个mp4文件。那么这个要如何实现了? 一. 技术方案 要完成这些功能&a…...

深度学习9--目标检测
1.概念介绍 目标检测不仅可以检测数字,而且可以检测动物的种类、汽车的种类等。例如,自动驾驶车辆需要自动识别前方物体是车辆还是行人,需要自动识别道路两 旁的指示牌和前方的红绿灯颜色。对于自动检测的算法,有两个要求…...

第131天:内网安全-横向移动Kerberos 攻击SPN扫描WinRMWinRSRDP
案例一:域横向移动-RDP-明文&NTLM RDP利用的三种方式 1.直接在当前被控主机上进行远程连接 2.建立节点进行连接 3.端口转发,(访问当前主机的2222端口等于访问目标的3389) 第一种方式(动静太大) 直接利用被控主机进行远程连接…...
微信小程序的四种弹窗使用
在做小程序的过程中,弹窗也算是非常实用的功能了,这几天写的几个功能就用到了弹窗,也可能是初学者的问题,比较菜,想找一个可以带图片的自定义的弹窗,,这里简单介绍一下官方封装好的四个弹窗…...

我的第一个CUDA程序
MatAdd算法 实现两个矩阵对应元素相加 #include <stdio.h> #include <stdlib.h>// 矩阵加法函数 void MatAdd(int height, int width) {// 在主机内存中为 A、B 和 C 分配内存float* A (float*)malloc(height * width * sizeof(float));float* B (float*)malloc…...
workerman下的webman路由浏览器跨域的一种问题
软件版本 "php": ">7.2", "workerman/webman-framework": "^1.5.0",问题情景 使用“分组路由”做API接口前后端分离跨域,在接口测试工具调试是能正常获取数据的;但在网页浏览器上调试就遇到了CORS、404的错…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...