js性能优化(五)

第五章开始啦~~~~~~~~~~~~~

防抖和节流之前自己有学过一次,包括几种方式怎么实现,代码如何写花了两天有写过,这次算是更系统的一个复习加填补

十七、防抖与节流

为什么需要防抖和节流:

在一些高频率事件触发的场景下我们不希望对应的事件处理函数多次执行

场景:

滚动事件(滚轮)
输入的模糊匹配
轮播图切换
点击操作

为什么会出现这样的情况?

浏览器默认情况下都会有自己的监听事件间隔( 4~6ms),如果检测到多次事件的监听执行,那么就会造成不必要的资源浪费,所以这里需要一种机制,就是防抖和节流

前置的场景: 界面上有一个按钮,我们可以连续多次点击

防抖:对于这个高频的操作来说,我们只希望识别一次点击,可以人为是第一次或者是最后一次

节流:对于高频操作,我们可以自己来设置频率,让本来会执行很多次的事件触发,按着我们定义的频率减少触发的次数

1.防抖实现

/** * handle 最终需要执行的事件监听* wait 事件触发之后多久开始执行,不写形参是为了方法的形参等问题的处理较为麻烦* immediate 控制执行第一次还是最后一次,false 执行最后一次
*/
function myDebounce(handle, wait, immediate) {// 参数类型判断及默认值处理if (typeof handle !== 'function') throw new Error('handle must be an function')if (typeof wait === 'undefined') wait = 300if (typeof wait === 'boolean') {immediate = waitwait = 300}if (typeof immediate !== 'boolean') immediate = false// 所谓的防抖效果我们想要实现的就是有一个 ”人“ 可以管理 handle 的执行次数// 如果我们想要执行最后一次,那就意味着无论我们当前点击了多少次,前面的N-1次都无用// return function的时候直接初始化的返回了一个函数赋值给了按钮的点击事件,然后点击按钮的时候//this和...args是这两个参数才会赋值给这个函数,所以不用箭头函数可以获取到this和argslet timer = nullreturn function proxy(...args) {let self = this,init = immediate && !timerclearTimeout(timer)timer = setTimeout(() => {timer = null!immediate ? handle.call(self, ...args) : null}, wait)
十七、十七、// 如果当前传递进来的是 true 就表示我们需要立即执行// 如果想要实现只在第一次执行,那么可以添加上 timer 为 null 做为判断// 因为只要 timer 为 Null 就意味着没有第二次....点击init ? handle.call(self, ...args) : null}
}// 定义事件执行函数
function btnClick(ev) {console.log('点击了1111', this, ev)
}// 当我们执行了按钮点击之后就会执行...返回的 proxy
let oBtn = document.getElementById('btn')
oBtn.onclick = myDebounce(btnClick, 200, false)

2.节流实现

节流:我们这里的节流指的就是在自定义的一段时间内让事件进行触发

核心的思想,就是给事件赋值函数的时候不直接赋值相应的直行函数,而是赋值一个代理函数

以滚动事件触发为例:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>节流函数实现</title><style>body {height: 5000px;}</style>
</head><body><script>// 节流:我们这里的节流指的就是在自定义的一段时间内让事件进行触发function myThrottle(handle, wait) {if (typeof handle !== 'function') throw new Error('handle must be an function')if (typeof wait === 'undefined') wait = 400let previous = 0  // 定义变量记录上一次执行时的时间 let timer = null  // 用它来管理定时器return function proxy(...args) {let now = new Date() // 定义变量记录当前次执行的时刻时间点let self = thislet interval = wait - (now - previous)if (interval <= 0) {// 此时就说明是一个非高频次操作,可以执行 handle clearTimeout(timer)timer = nullhandle.call(self, ...args)previous = new Date()} else if (!timer) {// 当我们发现当前系统中有一个定时器了,就意味着我们不需要再开启定时器// 此时就说明这次的操作发生在了我们定义的频次时间范围内,那就不应该执行 handle// 这个时候我们就可以自定义一个定时器,让 handle 在 interval 之后去执行 timer = setTimeout(() => {clearTimeout(timer) // 这个操作只是将系统中的定时器清除了,但是 timer 中的值还在timer = nullhandle.call(self, ...args)previous = new Date()}, interval)}}}// 定义滚动事件监听function scrollFn() {console.log('滚动了')}// window.onscroll = scrollFnwindow.onscroll = myThrottle(scrollFn, 600)</script>
</body></html>

每一次触发都会开辟一个全新的上下文,因此这里会造成性能上的消耗,造成不必要的资源浪费,因为我们也没必要有这么多的高频触发。

谷歌浏览器会在每5到6ms的时候进行一次事件监听,但是我们不想触发太频繁,因此这里我们想要400ms的时候再去触发,就可以写节流.

这里有一些注意点

  1. 写timeout的原因是,当我们处于高频出发时间段内的时候,我们是让他不执行,但是如果后续没有再触发,其实最后一次我们是需要相应的数据的,所以写timeout延迟触发最后一次
  2. 在设立定时器的时候需要将之前的定时器进行清除,不然会设立多个,只会延迟后面的操作,却不会不执行
  3. 谷歌浏览器的触发时间点正好跟咱们写的节流函数出发点时间重合的时候,interval <=0成立,但是tiemer确实不为空,定时函数就没有再重新设立,所以需要在上面的判断中,将timer重新清除,这样才可以形成一个新的定时器,为后续使用
  4. 这里的节流跟我之前看到的节流的实现可能不太一样,但是这一版本确实完善一些,主体思想都是一样的
    十八、减少判断层级

在编写代码的过程中避免不了会用到if else判断,但是多层的if判断会影响代码性能,这时我们都可以提前去return掉无效条件,达到嵌套层级的优化效果。

实例原代码:

/*** 编写一些章节会员判断* @param part 当前章节* @param chapter 当前章节数*/
function doSomeThing(part,chapter){const parts=['ES2016','工程化','Vue','React','Node'];if (part){if (parts.includes(part)){console.log('当前属于可读模块')if (chapter > 5){console.log('该模块是付费内容,您需要提供一个vip身份')}}}else{console.log('请确认模块信息')}
}
doSomeThing('ES2016',6)

简化:

function doSomeThing2(part, chapter) {const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node'];if (!part) {console.log('请确认模块信息')return}if (!parts.includes(part)) returnconsole.log('当前属于可读模块')if (chapter > 5) {console.log('该模块是付费内容,您需要提供一个vip身份')}
}
doSomeThing2('ES2016', 6)

在JSBench中进行测试:
在这里插入图片描述
可以看到下面的ops会大一些。

十九、减少循环体活动

主要说功能,而不是哪种实现方式,循环次数固定的情况下,放在循环体里的内容越多,执行效率越慢。有几种优化思路:

1.每次循环都要用到的不变的值,都抽离到循环外面去完成,类似于数据缓存。

例子:

var test = () =>{var i;var arr = ['zce','38','前端致胜'];for (i = 0; i <arr.length ; i++){console.log(arr[i])}
}
test()

优化:

var test2 = () =>{var i;var arr = ['zce','38','前端致胜'];var len = arr.length;//减少对象的访问层级for (i = 0; i <len ; i++){console.log(arr[i])}
}
test2()

运行比较:
在这里插入图片描述
2.若是我们对于输出顺序没有要求的话,也可以换成while循环,反向进行遍历。

var test3 = () =>{var arr = ['zce','38','前端致胜'];var len = arr.length;//减少对象的访问层级while(len--){console.log(arr[len])}
}
test3()

在这里插入图片描述

  • 代码量变少了
  • 从后往前,可以少做很多条件判断
  • 效率上如图也提高了
    二十、减少循环体活动

不同的数据声明方式在性能上面的关系和表现。

引用类型为例:

构造函数的方式:

let test = () =>{let obj = new Object();//构造函数的方式obj.name = 'zce'obj.age = 38obj.slogan = '前端致胜'return obj
}
console.log(test())

字面量方式:

let test2 = () =>{//字面量方式let obj = {name : 'zce',age : 38,slogan : '前端致胜'};return obj
}
console.log(test2())

对比:
在这里插入图片描述
还是能够看出来字面量的方式效率上更快,有一个较大的差异,原因:

  1. 构造函数声明的方式其实是调用一个函数,而下面字面量的方式是开辟一个空间
  2. 且代码多
    基础数据类型:
var str1 = 'zce说前端致胜'
var str2 = new String('zce说前端致胜')
console.log(str1)
console.log(str2)//zce说前端致胜
//[String: 'zce说前端致胜']

对比:
在这里插入图片描述
差距在5%以上,相较于引用类型数据,差距会更大,因为此时代码量是一致的,而上面是基础数据声明,下面是对象声明。

此时有一个细节,若是我们对这个字符串进行一些方法的调用的时候,上面会默认先转化为对象,然后再调用,而下面是直接调用,还是提倡用上面的方式,因为下面的方式创建的时候必然会有一些用不到的空间被占用和消耗。

总结:尽量采取字面量进行声明

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

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

相关文章

【Redis深度解析】揭秘Cluster(集群):原理、机制与实战优化

Redis Cluster是Redis官方提供的分布式解决方案&#xff0c;通过数据分片与节点间通信机制&#xff0c;实现了水平扩展、高可用与数据容灾。本文将深入剖析Redis Cluster的工作原理、核心机制&#xff0c;并结合实战经验分享优化策略&#xff0c;为您打造坚实可靠的Redis分布式…

Leetcode二叉树刷题

给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true public boolean isSymmetric(TreeNode root) {if(rootnull)return true;return compare(root.left,root.right);}public boole…

浏览器渲染原理-解释回流重绘以及为什么transform效率高

浏览器是如何渲染页面 当浏览器的网络线程收到 HTML 文档后&#xff0c;会产生一个渲染任务&#xff0c;并将其传递给渲染主线程的消息队列。在事件循环机制的作用下&#xff0c;渲染主线程取出消息队列中的渲染任务&#xff0c;开启染流程。 整个渲染流程分为多个阶段&#xf…

家居网购项目(权限验证+事务管理)

文章目录 1.过滤器权限认证1.程序框架图2.web.xml3.编写AdminAuthorization4.编写MemberAuthorization5.细节6.结果展示1.未登录可以任意浏览商品2.点击添加购物车提示登录3.点击后台管理&#xff0c;提示管理员登录4.也做了其余资源的访问验证 2.事务管理1.思路分析2.重写JDBC…

git am XXX.patch 文件内容解析

git am XXX.patch 文件内容解析 打补丁的两种方式&#xff1a; 1.patch XXX.patch 2.git am XXX.patch 例如&#xff1a; diff --git a/drivers/crypto/se/ce.c b/drivers/crypto/se/ce.c index e6f68286d4ce6..de1bcb46fbe6b 100644 --- a/drivers/crypto/se/ce.cb/drive…

品牌百度百科词条创建多少钱?

百度百科作为国内最具权威和影响力的知识型平台&#xff0c;吸引了无数品牌和企业争相入驻。一个品牌的百度百科词条&#xff0c;不仅是对品牌形象的一种提升&#xff0c;更是增加品牌曝光度、提高品牌知名度的重要途径。品牌百度百科词条创建多少钱&#xff0c;这成为了许多企…

【vue】ref 和 reactive 对比

ref&#xff1a;存储单个数据&#xff0c;如数值&#xff0c;字符串reactive&#xff1a;存储复杂数据&#xff0c;如对象&#xff0c;数组 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"vie…

《QT实用小工具·二十六》运行时间记录

1、概述 源码放在文章末尾 运行时间记录&#xff0c;包含如下功能&#xff1a; 可以启动和停止服务&#xff0c;在需要的时候启动。 可以指定日志文件存放目录。 可以指定时间日志输出间隔。 可以单独追加一条记录到日志文件。 日志为文本格式&#xff0c;清晰明了。 软…

《前端面试题》- JS基础 - 伪数组

第一次听说伪数组这个概念&#xff0c;听到的时候还以为是说CSS的伪类呢&#xff0c;网上一查&#xff0c;这东西原来还是个很常见的家伙。 何为伪数组 伪数组有两个特点&#xff1a; 具有length属性&#xff0c;其他属性&#xff08;索引&#xff09;为非负整数但是却不具备…

使用DockerCompose配置基于哨兵模式的redis主从架构集群

文章目录 一、注意事项&#xff08;坑点&#xff01;&#xff01;&#xff01;&#xff09;二、配置Redis主从架构集群第一步&#xff1a;创建目录文件结构第二步&#xff1a;编写DockerCompose配置文件第三步&#xff1a;编写redis.conf第四步&#xff1a;启动redis主从集群 三…

Kubernetes 升级不弃 Docker:KubeKey 的丝滑之道

作者&#xff1a;尹珉&#xff0c;KubeSphere Ambaasador&Contributor&#xff0c;KubeSphere 社区用户委员会杭州站站长。 引言 随着 Kubernetes 社区的不断发展&#xff0c;即将迎来 Kubernetes 1.30 版本的迭代。在早先的 1.24 版本中&#xff0c;社区作出一个重要决策…

SysTick滴答定时器 - 延时函数

SysTick定时器 Systick定时器&#xff0c;是一个简单的定时器&#xff0c;对于CM3,CM4内核芯片&#xff0c;都有Systick定时器。Systick定时器常用来做延时&#xff0c;或者实时系统的心跳时钟。这样可以节省MCU资源&#xff0c;不用浪费一个定时器。比如UCOS中&#xff0c;分…

Windows10为Git Bash添加文件传输命令rsync(详细图文配置)

文章目录 1. 安装git bash2. 下载所需要的4个包3. 下载解压包的软件4. 复制每个包下面的usr到git安装目录下4.1 所遇问题4.2 解决 5. 安装完成6. 需要注意 Windows上要使用 rsync命令上传或下载文件&#xff0c;需要使用git bash&#xff0c;git bash没有rsync&#xff0c;需要…

MAC(M1芯片)编译Java项目慢且发热严重问题解决方案

目录 一、背景二、排查三、解决四、效果以及结果展示五、总结 一、背景 使用idea编译项目等操作&#xff0c;经常性发热严重&#xff0c;并且时间慢。直到昨天编译一个项目用时30分钟&#xff0c;电脑温度很高&#xff0c;并且有烧灼的味道&#xff0c;于是有了此篇文章。 二、…

Python的国际化和本地化【第162篇—国际化和本地化】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 随着全球化的发展&#xff0c;多语言支持在软件开发中变得越来越重要。Python作为一种流行的…

VRRP——虚拟路由冗余协议

什么是VRRP 虚拟路由冗余协议VRRP&#xff08;Virtual Router Redundancy Protocol&#xff09;是一种用于提高网络可靠性的容错协议。 通过VRRP&#xff0c;可以在主机的下一跳设备出现故障时&#xff0c;及时将业务切换到备份设备&#xff0c;从而保障网络通信的连续性和可…

【vue】用vite创建vue项目

前置要求 要有Node.js 1. 用vite创建vue项目 在cmd中&#xff0c;进入一个文件夹 在文件资源管理器上面的文件目录中&#xff0c;输入cmd&#xff0c;回车在cmd中通过cd命令进入对应文件夹 创建项目 npm create vitelatest # 创建项目创建项目过程中的一些选项 Ok to pro…

06-vscode+espidf开发调试方法(内置JTAG调试)

使用VS Code和ESP-IDF进行ESP32开发和调试 在我们搭建 IDF 框架后&#xff0c;OpenOCD 已经自动下载好了&#xff0c; 我们通过 JTAG 接口连接使用 OpenOCD 进行调试。而ESP32芯片中内置 了JTAG 电路&#xff0c;无需额外芯片即可调试&#xff0c;更加方便&#xff0c;所以这里…

MySQL表结构的操作

文章目录 1. 创建表2. 查看表3. 修改表4. 删除表 1. 创建表 create table table_name (field1 datatype,field2 datatype,field3 datatype )character set 字符集 collate 校验集 engine 存储引擎;field&#xff1a;列名datatype&#xff1a;列的类型character set&#xff1a…

使用深度学习集成模型进行乳腺癌组织病理学图像分类

基于预训练的VGG16和VGG19架构训练了四种不同的模型&#xff08;即完全训练的 VGG16、微调的 VGG16、完全训练的 VGG19 和微调的 VGG19 模型&#xff09;。最初&#xff0c;我们对所有单独的模型进行了5倍交叉验证操作。然后&#xff0c;我们采用集成策略&#xff0c;取预测概率…