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 = 300
  if (typeof wait === 'boolean') {
    immediate = wait
    wait = 300
  }
  if (typeof immediate !== 'boolean') immediate = false

  // 所谓的防抖效果我们想要实现的就是有一个 ”人“ 可以管理 handle 的执行次数
  // 如果我们想要执行最后一次,那就意味着无论我们当前点击了多少次,前面的N-1次都无用
  // return function的时候直接初始化的返回了一个函数赋值给了按钮的点击事件,然后点击按钮的时候
  //this和...args是这两个参数才会赋值给这个函数,所以不用箭头函数可以获取到this和args
  let timer = null
  return function proxy(...args) {
    let self = this,init = immediate && !timer
    clearTimeout(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 = 400

      let previous = 0  // 定义变量记录上一次执行时的时间 
      let timer = null  // 用它来管理定时器

      return function proxy(...args) {
        let now = new Date() // 定义变量记录当前次执行的时刻时间点
        let self = this
        let interval = wait - (now - previous)

        if (interval <= 0) {
          // 此时就说明是一个非高频次操作,可以执行 handle 
          clearTimeout(timer)
          timer = null
          handle.call(self, ...args)
          previous = new Date()
        } else if (!timer) {
          // 当我们发现当前系统中有一个定时器了,就意味着我们不需要再开启定时器
          // 此时就说明这次的操作发生在了我们定义的频次时间范围内,那就不应该执行 handle
          // 这个时候我们就可以自定义一个定时器,让 handle 在 interval 之后去执行 
          timer = setTimeout(() => {
            clearTimeout(timer) // 这个操作只是将系统中的定时器清除了,但是 timer 中的值还在
            timer = null
            handle.call(self, ...args)
            previous = new Date()
          }, interval)
        }
      }

    }

    // 定义滚动事件监听
    function scrollFn() {
      console.log('滚动了')
    }

    // window.onscroll = scrollFn
    window.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)) return
    console.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 = 38
    obj.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%以上,相较于引用类型数据,差距会更大,因为此时代码量是一致的,而上面是基础数据声明,下面是对象声明。

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机

STC89C52 一.定时器 1.介绍 2.计时 2.定时器寄存器  2.1 定时器控制寄存器TCON  2.2 定时器模式寄存器TMOD  2.3 定时器如何定时10毫秒  2.4 定时器寄存器配置    2.4.1 TCON    2.4.2 TMOD    2.4.3 实现    2.4.5 按位操作 3.定时器中断  3.1 定…

d盘无法格式化说另一个正在使用怎么办

在日常生活和工作中&#xff0c;我们经常会遇到需要对电脑硬盘进行格式化的情况。然而&#xff0c;有时在尝试格式化D盘时&#xff0c;会遇到一个常见的错误提示&#xff1a;“另一个程序正在使用此文件&#xff0c;因此无法进行操作”。这个提示可能会让许多人感到困惑&#x…

《自动机理论、语言和计算导论》阅读笔记:p172-p224

《自动机理论、语言和计算导论》学习第 8 天&#xff0c;p172-p224总结&#xff0c;总计 53 页。 一、技术总结 1.Context-Free Grammar(CFG) 2.parse tree (1)定义 p183&#xff0c;But perhaps more importantly, the tree, known as a “parse tree”, when used in a …

C语言基础入门案例(1)

目录 第一题&#xff1a;实现大衍数列的打印 第二题&#xff1a;生成所有由1、2、3、4组成的互不相同且无重复数字的三位数&#xff0c;并计算总数 第三题&#xff1a;整数加法计算器 第四题&#xff1a;实现一个范围累加和函数 第五题&#xff1a;编写一个函数计算整数的阶…

Vue3基础笔记(3)高级绑定

一.Class绑定 数据绑定的一个常见需求场景师操纵元素的CSS class列表&#xff0c;因为class是attribute&#xff0c;我们可以和其他attribute一样使用v-bind将他们和动态的字符串绑定&#xff0c;但是在处理较为复杂的绑定时&#xff0c;拼接字符串容易出现错误。因此Vue专门为…

ZJJ-2A直流绝缘监视继电器额定电流3.1mA额定电压110VDCJOSEF约瑟

系列型号 JJJ-1绝缘监视继电器&#xff1b; ZJJ-1/A绝缘监视继电器&#xff1b; ZJJ-1A绝缘监视继电器&#xff1b; ZJJ-2型直流绝缘监视继电器 ZJJ-2直流绝缘监视继电器&#xff1b; ZJJ-2B直流绝缘监视继电器&#xff1b; ZJJ-2AC直流绝缘监视继电器&#xff1b; 用途…

【基础物理实验】【AFM虚拟实验】基于AFM的物质表面微观结构及力学性质表征仿真实验(上)【北京航空航天大学】

基于AFM的物质表面微观结构及力学性质表征仿真实验 说明&#xff1a; 本次实验为本科生《基础物理实验》课程中的虚拟实验部分&#xff0c;在虚拟实验平台中进行。 一、实验目的&#xff1a; 1. 掌握AFM的基本成像原理及系统结构&#xff1b; 2. 掌握AFM的基本操作技巧及操…

什么是云安全

云安全和网络安全有所不同&#xff0c;因为云安全一词 比网络安全更涵盖整个企业基础设施。一般来说&#xff0c;当人们提到云安全时&#xff0c;指的是第三方服务提供商提供的 IaaS 云环境。在这种情况下&#xff0c;云安全不仅包括网络安全工具&#xff0c;还包括服务器、容器…

数据结构7:栈

文章目录 头文件Stack.h 实现文件 测试文件test.c 经典题目 头文件 Stack.h #pragma once #include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h>#define CAPACITY_INIT 4typedef int STDataType;typedef struct Stack {STDa…

云端运维:我的成长轨迹与荣耀印记

云端运维&#xff1a;我的成长轨迹与荣耀印记 关于博主 ​ 我是一名运维领域的博客平台博主&#xff0c;也是一名对技术充满热情的探索者。在运维这条道路上&#xff0c;我不断积累知识、提升技能&#xff0c;力求将最新的技术理念和实践经验分享给更多志同道合的朋友。我热衷…

Python代码打包成exe程序

国内镜像源 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple阿里云 https://mirrors.aliyun.com/pypi/simple/豆瓣 https://pypi.douban.com/simple/ 百度云 https://mirror.baidu.com/pypi/simple/中科大 https://pypi.mirrors.ustc.edu.cn/simple/华为云 https://mirror…

Jmeter参数化的 4 种方式用法总结

参数化就是用变量代替数据的过程&#xff0c;总结参数化的4种方式&#xff1a; 1、用户自定义变量 用户自定义变更有两种方法&#xff1a; &#xff08;1&#xff09;在测试计划面板中的用户定义的变量设置 说明&#xff1a;在此用户定义的变量对所有测试计划都会生效 &…

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

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

Python数据容器(一)

一.数据容器入门 1.Python中的数据容器&#xff1a;一种可以容纳多份数据的数据类型&#xff0c;容纳的每一份数据称之为1个元素&#xff0c;每一个元素&#xff0c;可以是任意类型的数据&#xff0c;如字符串、数字、布尔等。 2.数据容器根据特点的不同&#xff0c;如&#…

2A大电流线性稳压器具备两种输出电压范围

概述 PCD3931 是一款低噪声、低压差线性稳压器 (LDO)&#xff0c;可提供 2A 输出电流&#xff0c;最大压降仅为 160mV。该器件提供两种输出电压范围。 PCD3931 的输出电压可通过外部电阻分压器在 0.5V 至 5.5V 范围内进行调节。PCD3931 集低噪声、高 PSRR 和高输出电流能力等特…

24年重庆三支一扶报名照不通过怎么处理?

24年重庆三支一扶报名照不通过怎么处理&#xff1f;

消化内科专科护士理论考试案例选择题及答案

电大搜题 多的用不完的题库&#xff0c;支持文字、图片搜题&#xff0c;包含国家开放大学、广东开放大学、超星等等多个平台题库&#xff0c;考试作业必备神器。 公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#…

鸿蒙 UI预览报错

SyntaxError: Unexpected end of JSON input 删除entry下的.preview文件 重新刷新预览

python爬虫------- Selenium下篇(二十三天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

MySQL表空间管理与优化(8/16)

表空间管理和优化 innodb_file_per_table参数&#xff08;此参数在分区表章节中还会出现&#xff09;&#xff1a; 这个参数决定了InnoDB表数据的存储方式。当参数设置为ON时&#xff0c;每个InnoDB表的数据会单独存储在一个以.ibd为后缀的文件中&#xff0c;这有利于管理和回收…