JavaScript性能优化8——防抖与节流

目录

一、引入

为什么需要防抖和节流

使用场景

概念

二、防抖函数实现

场景

原始代码

防抖函数代码

三、节流函数实现

场景

原始代码

节流函数代码


一、引入

为什么需要防抖和节流

我们现在JS主要是在浏览器下运行,涉及到很多人机交互的操作。假设一个场景,我们打开的网页有一个轮播图,我们对左右切换的按钮疯狂点击。按钮是绑定了操作的,那么每次我们点击这个按钮被监听到后就会执行代码,有部分内存会被使用,性能也有所消耗。这就是所谓的高频次触发的场景。在这种场景下,我们希望事件对应的监听不需要立即或者说反复被触发,这样我们就需要对其进行防抖和节流的操作。

使用场景

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

浏览器默认情况下都会有自己的监听事件间隔,比如Chrome浏览器监听间隔是4-6ms。如果检测到多次事件的监听执行,那么就会造成不必要的资源浪费。

概念

防抖:对于高频操作来说,我们只希望只识别一次,可以认为认为是第一次或者最后一次。

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

二、防抖函数实现

场景

有一个按钮,我们对其进行高频触发,但是希望只识别一次。

原始代码

这个代码下,就是点击一次执行一次。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>防抖函数实现</title>
    </head>
    <body>
        <button id="btn" >点击</button>
        <script>
            //找到目标元素
            var oBtn = document.getElementById('btn')
            oBtn.onclick = function () {
                console.log('点击了')
            }
        </script>
    </body>
</html>

防抖函数代码

里面具体的思路和每一块是干什么的,都写在代码注释里面了,这边就不展开了。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>防抖函数实现</title>
    </head>
    <body>
        <button id="btn" >点击</button>
        <script>
            //找到目标元素
            var oBtn = document.getElementById('btn')
            // oBtn.onclick = function () {
            //     console.log('点击了')
            // }
            /**
            * handle 要执行的操作
            * wait 事件触发后多久开始执行
            * immediate 控制执行第一次还是最后一次,如果是false就执行最后一次,如果是true就执行第一次
            */
            // 其实这些参数我们可以考虑给他们添加默认值比如wait = 300, immediate = false。
            // 但是定义一个函数给它的形参赋予一个默认值,这样的话它整个函数体里的代码将来如果出现一些变量定义之后,会有一些比较变态的机制.这边不做讨论,所以先不设置默认值了
            function myDebounce (handle,wait,immediate) {
                //参数类型判断及默认值处理
                if(typeof handle !== 'function') throw new Error('handle must be a function')
                if(typeof wait === 'undefined') wait = 300
                if(typeof wait === 'boolean') {
                    immediate = wait
                    wait = 300
                }
                if(typeof immediate === 'undefined') immediate = false

                // 所谓的防抖效果,我们想要实现的是有一个”人“,可以管理handle的执行次数
                // 如果我们想要执行最后一次,那意味着无论我们当前点击了多少次,前面的N-1次都没有用
                let timer = null
                return function proxy(...args) {
                    let self = this
                    init = immediate && !timer
                    clearTimeout(timer)
                    timer = setTimeout(() => {
                        timer = null
                        //只有我们的immediate为false才执行下面这个代码,否则就不执行【原本其实就是handle(),但是为了拿到this和mouseEvent所以改成下面这个样子】
                        !immediate ? handle.call(self, ...args) : null
                    },wait)
                    //如果当前传进来的是true,就表示我们需要立即执行
                    // 如果想要实现只在第一次执行,可以添加上timer为null作为判断,因为只要timer为null,就意味着没有第二次
                    init ? handle.call(self, ...args) : null
                }
            }
            // 定义事件执行函数,这里的ev是点击的MouseEvent,this是button元素
            function btnClick(ev) {
                console.log('点击了', this, ev)
            }
            // 当我们执行了按钮点击之后就会执行防抖函数返回的proxy
            //oBtn.onclick = myDebounce(btnClick,false)
            oBtn.onclick = myDebounce(btnClick,200,true)
        </script>
    </body>
</html>

三、节流函数实现

场景

一个页面我们往下滚动,触发相应事件。

原始代码

这个代码下,我们只要做出滚动的动作就会被监听到,不断触发scrollFn方法。但是这其实是会造成资源浪费的,我们并不需要这么高频次的响应触发。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>节流函数实现</title>
        <style>
            body {
                height:5000px
            }
        </style>
    </head>
    <body>
        <script>
            // 定义滚动事件监听
            function scrollFn() {
                console.log('滚动了')
            }
            window.onscroll = scrollFn
        </script>
    </body>
</html>

节流函数代码

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

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <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 a function')
                if(typeof wait === 'undefined') wait = 400

                let previous = 0 // 定义变量记录上一次执行的时间
                let timer = null // 用来管理定时器
                return function proxy(...args) {
                    let self = this
                    let now = new Date()
                    let interval = wait - ( now - previous)
                    if(interval <= 0){
                        //万一很巧的定时器延迟到的操作和我们的点击同时进行
                        clearTimeout(timer)
                        timer = null
                        //非高频次操作,可以执行handle
                        handle.call(self, ...args)
                        previous = new Date()
                    }
                    //当我们发现系统中有一个定时器了,就不需要再开启定时器了
                    else if(!timer){
                        //这次操作发生在我们定义的频次时间范围内,不应该执行
                        // 这个时候可以自己定义一个定时器,让handle在interval之后去执行
                        timer = setTimeout(() => {
                            clearTimeout(timer)// 这个操作知识将系统中的定时器清楚了,但是timer中的值还在,所以需要手动吧timer = null
                            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>

参考资料

1.拉勾网 《大前端训练营》课程

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值