js的防抖与节流

目录

  • 认识防抖与节流
    • 防抖
    • 节流
  • 手写防抖函数
    • 绑定this与参数
    • 取消功能
    • 立即执行
    • 获取返回值
    • 最终版
  • 手写节流函数

认识防抖与节流

JavaScript中,大量操作都会触发事件,这些事件又会被添加到事件队列中进行排队处理
某些事件如果频繁触发的话会对浏览器的性能造成损耗,我们就可以使用防抖或者节流操作来限制事件的执行频率

防抖

防抖即当一个事件被触发时不会立即执行,而是会等待一段时间后执行,在等待期间如果此事件被重复触发,则等待时间也会重新计算,只有在等待期间内没有事件触发,等待时间结束后才会触发事件的回调函数
简单地说,王者里的回城就是一种防抖

节流

节流即当事件触发时会执行事件的回调函数,之后这个事件将会等待一段时间,在这段时间内触发的事件都将不会执行,直到等待时间结束后如果依旧此事件触发才会执行此事件的回调函数,之后继续等待
简单地说,王者里的技能就是一种节流

如下图所示
结果

手写防抖函数

const debounce = (fn, delay) => {
    let timer = null
    return () => {
        timer && clearTimeout(timer)
        timer = setTimeout(fn, delay)
    }
}
const resize = debounce(() => {
    console.log('resize')
}, 1000)
window.addEventListener('resize', resize)

结果如下
结果
但这个函数十分简陋,没有绑定this,也没有参数传递,接下来我们一步一步来实现这些功能

绑定this与参数

绑定this有两种方式,一种是通过在参数列表中传入this指向,然后通过显式绑定到对应的对象上,还有一种是将返回的箭头函数改成普通函数

  1. 第一种方式
const debounce = (fn, {delay,that}={}) => {
    let timer = null
    return () => {
        timer && clearTimeout(timer)
        timer = setTimeout(fn.apply(that), delay)
    }
}
const resize = debounce(() => {
    console.log(this)

}, {
    delay:1000,
    that:window
})
window.addEventListener('resize', resize)

这段代码粗看似乎没什么问题,但实测发现防抖函数无效,因为apply会立即调用函数,解决方法是将apply封装到一个函数中

const debounce = (fn, { delay, that } = {}) => {
    let timer = null
    return () => {
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(that)
        }, delay)
    }
}
const resize = debounce(() => {
    console.log(this)

}, {
    delay: 1000,
    that: window
})
window.addEventListener('resize', resize)

当我们需要传递的参数过多时可以通过参数解构与默认值的方式获取
2. 第二种方式

const debounce = (fn, delay) => {
    let timer = null
    return function () {
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this)
        }, delay)
    }
}
const resize = debounce(() => {
    console.log(this)

}, 1000)
window.addEventListener('resize', resize)

最后结果都是一样的
结果
参数的绑定十分简单,这里就不再赘述了

const debounce = (fn, delay) => {
    let timer = null
    return function (...args) {
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, delay)
    }
}
const resize = debounce((event) => {
    console.log(this, event)
}, 1000)
window.addEventListener('resize', resize)

取消功能

有时候事件触发了但我们之后又不想函数执行,可以增加一个取消功能,我们可以在返回的函数上直接添加一个属性cancel

const debounce = (fn, delay) => {
    let timer = null
    const _debounce = function (...args) {
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, delay)
    }
    _debounce.cancel = function () {
        timer && clearTimeout(timer)
    }
    return _debounce
}
const resize = debounce((event) => {
    console.log(this, event)
}, 1000)
window.addEventListener('resize', resize)

立即执行

立即执行即我们需要在事件触发时的第一次就执行函数

const debounce = (fn, { delay = 1000, immediate = false } = {}) => {
    let timer = null
    let isInvoke = false

    const _debounce = function (...args) {
        if (immediate && !isInvoke) {
            fn.apply(this, args)
            isInvoke = true
            return
        }
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, delay)
    }
    _debounce.cancel = function () {
        timer && clearTimeout(timer)
    }
    return _debounce
}
const resize = debounce((event) => {
    console.log(this, event)
}, {
    delay: 1000,
    immediate: true
})
window.addEventListener('resize', resize)

结果

获取返回值

有时候我们在手动调用防抖函数的时候需要得到函数的返回值就可以这么写,第一种方案是通过回调函数,第二种则是返回一个Promise

  1. 传入一个回调函数来获取返回值
const debounce = (fn, { delay = 1000, immediate = false, callback } = {}) => {
    let timer = null
    let isInvoke = false

    const _debounce = function (...args) {
        if (immediate && !isInvoke) {
            fn.apply(this, args)
            isInvoke = true
            return
        }
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            let res = fn.apply(this, args)
            callback && callback(res)
        }, delay)
    }
    _debounce.cancel = function () {
        timer && clearTimeout(timer)
    }
    return _debounce
}
const resize = debounce((event) => {
    console.log(this)
    return "resize return"
}, {
    delay: 1000,
    immediate: false,
    callback: (res) => {
        console.log(res)
    }
})
resize()
  1. 返回一个Promise得到返回值
const debounce = (fn, { delay = 1000, immediate = false } = {}) => {
    let timer = null
    let isInvoke = false

    const _debounce = function (...args) {
        return new Promise((resolve, reject) => {
            if (immediate && !isInvoke) {
                fn.apply(this, args)
                isInvoke = true
                return
            }
            timer && clearTimeout(timer)
            timer = setTimeout(() => {
                let res = fn.apply(this, args)
                resolve(res)
            }, delay)
        })
    }
    _debounce.cancel = function () {
        timer && clearTimeout(timer)
    }
    return _debounce
}
const resize = debounce((event) => {
    console.log(this)
    return "resize return"
}, {
    delay: 1000,
    immediate: false
})
resize().then((res) => {
    console.log(res)
})

结果都是一样的
结果

最终版

最后我们将以上代码优化一下就得到了最终版本的防抖函数

const debounce = (fn, { delay = 1000, immediate = false } = {}) => {
    let timer = null
    let isInvoke = false

    const _debounce = function (...args) {
        return new Promise((resolve, reject) => {
            try {
                if (immediate && !isInvoke) {
                    let res = fn.apply(this, args)
                    isInvoke = true
                    resolve(res)
                    return
                }
                timer && clearTimeout(timer)
                timer = setTimeout(() => {
                    let res = fn.apply(this, args)
                    timer = null
                    isInvoke = false
                    resolve(res)
                }, delay)
            } catch (error) {
                reject(error)
            }
        })
    }
    _debounce.cancel = function () {
        timer && clearTimeout(timer)
        isInvoke = false
        timer = null
    }
    return _debounce
}

手写节流函数

节流函数在实现上和防抖函数稍有不同,不通过定时器而是通过时间戳
以下是一个简略的节流函数实现

const throttle = (fn, { wait = 1000, }) => {
    let preTime = 0;
    const _throttle = function (...args) {
        let nowTime = Date.now();
        if (nowTime - preTime > wait) {
            fn.apply(this, args);
            preTime = nowTime;
        }
    }
    return _throttle
}
const resize = throttle(function () {
    console.log("resize")
}, {
    wait: 1000,
})
window.addEventListener('resize', resize)

结果如下
结果
至于节流函数的一些优化:

  1. 节流函数立即执行与尾部执行
  2. 添加取消功能
  3. 获得返回值

与防抖函数的思路大差不差,这里就不再过多赘述,以下是完全版

const throttle = (fn, { wait = 1000, leading = true, trailing = false } = {}) => {
    let preTime = 0;
    let timer
    const _throttle = function (...args) {
        return Promise((resolve, reject) => {
            try {
                let nowTime = Date.now();
                if (!leading && preTime == 0) {
                    preTime = nowTime
                }
                let interval = wait - (nowTime - preTime)
                if (interval <= 0) {
                    let res = fn.apply(this, args)
                    resolve(res)
                    if (timer) clearTimeout(timer)
                    preTime = nowTime
                    timer = null
                    return
                }
                if (trailing && !timer) {
                    timer = setTimeout(() => {
                        let res = fn.apply(this, args)
                        resolve(res)
                        preTime = Date.now()
                        timer = null
                    }, interval)
                }
            } catch (error) {
                reject(error)
            }
        })
    }
    _throttle.cancel = function () {
        if (timer) clearTimeout(timer)
        preTime = 0
        timer = null
    }
    return _throttle
}

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

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

相关文章

服务器变矿机,该如何应对?

开始 恶意的挖矿程序会导致服务器cpu的异常占用&#xff0c;很让人讨厌。起初&#xff0c;我只是使用top命令显示出占用cpu不正常的进程&#xff0c;发现其中一个进程占用了百分之九十九点几&#xff0c;然后通过kill -9 <PID>命令干掉它。但总是过不了几天&#xff0c;…

Windows系统字体尺寸学习

调用GetTextMetrics来获得字体尺寸信息, 函数返回设备描述表中当前选定的字体信息&#xff1b; 返回值到TEXTMETRIC类型的结构中&#xff1b; 返回字段值的单位取决于当前设备描述表映射方式&#xff1b;默认映射方式是MM_TEXT&#xff0c;值的单位是像素&#xff1b; 前7个字…

【MATLAB源码-第113期】基于matlab的孔雀优化算法(POA)机器人栅格路径规划,输出做短路径图和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 POA&#xff08;孔雀优化算法&#xff09;是一种基于孔雀羽毛开屏行为启发的优化算法。这种算法模仿孔雀通过展开其色彩斑斓的尾羽来吸引雌性的自然行为。在算法中&#xff0c;每个孔雀代表一个潜在的解决方案&#xff0c;而…

CSS3弹性盒布局详解

CSS3的弹性盒布局 简介 弹性盒&#xff08; Flexible Box 或 Flexbox&#xff09; 布局是CSS3提供的一种新的布局模式&#xff0c;是一种当页面需要适应不同的屏幕大小及设备类型时&#xff0c;确保元素拥有恰当行为的一种布局方式。 弹性盒的结构: 从图中所知&#xff0c…

K8s(一)Pod资源——Pod介绍、创建Pod、Pod简单资源配额

目录 Pod概述 pod网络 pod存储 pod和容器对比 创建pod的方式 pod运行方式分类 Pod的创建 Pod的创建过程 通过kubectl run来创建pod 通过yaml文件创建&#xff0c;yaml文件简单写法 Pod简单操作 Pod的标签labels Pod的资源配额resource 测试 Pod概述 Kubernetes …

嵌入式学习-网络编程-Day4

思维导图 广播通信模型 #include <myhead.h> int main(int argc, char const *argv[]) {int rfd socket(AF_INET,SOCK_DGRAM,0);if(rfd-1){perror("socket error");return -1;}//填充地址信息结构体struct sockaddr_in rin;rin.sin_familyAF_INET;rin.sin_por…

[ceph] ceph应用

一、资源池 Pool 管理 #创建一个 Pool 资源池&#xff0c;其名字为 mypool&#xff0c;PGs 数量设置为 64&#xff0c;设置 PGs 的同时还需要设置 PGP&#xff08;通常PGs和PGP的值是相同的&#xff09;&#xff1a; PG (Placement Group)&#xff0c;pg 是一个虚拟的概念&…

基于变换域的模版匹配

模板匹配原理 图像的空间域与其他域之间的变换&#xff0c;如傅里叶变换&#xff0c;小波变换&#xff0c;轮廓波变换&#xff0c;剪切波变换等&#xff0c;实际上是图像在其他坐标领域中的表现。在空间域中&#xff0c;图像的信息是像素值和坐标位置&#xff1b;在其他域中&a…

交通流量预测HSTGCNT:Hierarchical Spatio–Temporal Graph Convolutional

Hierarchical Spatio–Temporal Graph Convolutional Networks and Transformer Network for Traffic Flow Forecasting 交通流预测的层次时空图卷积网络和Transformer网络 Abstract 图卷积网络&#xff08;GCN&#xff09;具有图形化描述道路网络不规则拓扑结构的能力&#…

【C语言】数据结构——排序三(归并与计数排序)

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读&#xff1a;1. 归并排序1.1 基本思想1.2 递归实现1.3 非递归实现 2. 计数排序2.1 基本思想2.2 代码实现 导读&#x…

如何用Python进行数据分析(保姆级教程)

有小伙伴在学Python新手教程的时候说学Python比较复杂的地方就是资料太多了&#xff0c;比较复杂。 很多网上的资料都是从语法教起的&#xff0c;花了很多时间还是云里雾里&#xff0c;摸不清方向。今天就给大家来捋一捋思路&#xff01;帮助大家提高学习效率&#xff01; Pyt…

夜神安装Magisk及Delta(狐狸面具)教程

Magisk和LSPosed、EdXPosed下载 Magisk框架下载与安装教程 - 多开鸭Magisk及LSPosed在模拟器安装的详细视频教程 推荐先看一遍教程 视频教程 雷电模拟器版本教程&#xff1a;https://www.bilibili.com/video/BV1kv4y127af 夜神模拟https://www.duokaiya.com/magisk.html 夜神模…

JS遍历对象的方法及特点

1、定义一个对象 let obj {name: Tom,age: 20,sex: 男,};obj.weight 70kg;// obj的原型上定义属性Object.prototype.height 180cm;Object.prototype.major function() {console.log(专业&#xff1a;计算机应用技术);};console.log(obj, obj); 控制台输出的obj中&#xff…

C++make_pair,你真的懂了吗?

其实写这篇文章我还是很忐忑的&#xff0c;因为用C也写了快一年了&#xff0c;平时代码量个人认为还可以&#xff0c;但是最近两天频繁犯错&#xff0c;下面先说说我写的错误吧&#xff01; 我们都知道make_pair返回的是一个pair类型的函数&#xff0c;而pair这个键值对它又是…

go语言(一)----声明常量

package mainimport ("fmt""time" )func main() {fmt.Print("hello go!")time.Sleep(1 * time.Second)}运行后&#xff0c;结果如下&#xff1a; 1、golang表达式中&#xff0c;加&#xff1b;和不加&#xff1b;都可以 2、函数的{和函数名一…

二二复制模式玩法解析

这个模式和小编介绍的其他模式不同&#xff0c;其他的模式都是需要一个推荐来获得返利或者免单的&#xff0c;但是这个模式是不需要的&#xff0c;因为它可以依靠平台来完成闭环。 具体是怎么操作的呢&#xff1f;这个模式很简单&#xff0c;只有两个奖励。一个是直推奖&#x…

C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针

入门知识已经梳理完毕了&#xff0c;接下来就进入到面型对象的部分学习了 文章目录 1.面向过程和面向对象初步认识2.类的引入3.类的定义3.1类的结构3.2类的两种定义方式3.2.1声明和定义全部放在类体中3.2.2声明和定义分开 3.3成员变量命名规则的建议 4.类的访问限定符及封装4.1…

Java-NIO篇章(2)——Buffer缓冲区详解

Buffer类简介 Buffer类是一个抽象类&#xff0c;对应于Java的主要数据类型&#xff0c;在NIO中有8种缓冲区类&#xff0c;分别如下&#xff1a; ByteBuffer、 CharBuffer、 DoubleBuffer、 FloatBuffer、 IntBuffer、 LongBuffer、 ShortBuffer、MappedByteBuffer。 本文以它的…

【Linux】nc 网络诊断 | 文件传输 命令详解

目录 一、命令简介 二、命令使用 2.1 测试服务器 2.2 端口连通性测试 2.2.1tcp端口连通性测试 2.2.2udp端口连通性测试 2.3 文件及目录的传输 2.3.1 文件传输(TCP端口) 2.3.2 文件传输(UDP端口) 相关文章&#xff1a; 【网络】抓包工具Wireshark下载安装和基本使用教…

EasyConnect客户端 连接时提示,获取服务端配置信息失败

环境&#xff1a; EasyConnect客户端 问题描述&#xff1a; EasyConnect客户端 连接时提示&#xff0c;获取服务端配置信息失败 解决方案&#xff1a; 1.电脑上的防火墙和杀毒软件建议关闭,右键以管理员身份运行EasyConnect客户端使用(临时解决本案例) 2.用修复工具修复测…