• 防抖(debounce)和节流(throttle) 通俗说明 + 示例


    最近在面试,被问到防抖和节流的问题,虽然平时也有用电梯函数这种延时执行的代码,但是被问到概念的时候还是有点懵逼,所以赶紧来学习一下,分享一下学习成果,顺便把自己写的demo贴到下面,感兴趣的同学可以直接复制下面代码作为一个react组件引入到代码中,打开不同的注释掉的函数,看看效果,体验一下就明白了。

    防抖(debounce)和节流(throttle)

    首先明确一点防抖(debounce) 和 节流(throttle) 都是对函数执行的控制,因为某些事件会频繁的触发,例如 scroll mouseMove resize 等...

    • 防抖:是固定延时后执行,例如延时1s后执行一次,每次触发事件都会重置这个延时,防抖处理过的函数会推迟执行,假如你一直触发事件,防抖处理过的函数就一直不执行。
    • 节流:是固定时间执行一次,例如固定1秒执行一次,每次调用函数不会推迟执行,假如你一直触发事件,节流处理过的函数还是会1s执行一次。

    this 的指向问题 (延伸)

    • 箭头函数不绑定this,没有argument参数,它使用的this是作用域继承来的this
    • es5函数的this,指向函数的调用者
    • call, apply, bind 可以改变this的指向
    • argument参数是从执行的函数的作用域中获取的

    例子

    • 下面这个例子可以copy到react执行环境中查看效果,同时this的指向问题,也可以在代码中提现
    • 可以新建一个js文件把当前代码复制到xx.js 文件中,在其他地方引用该组件体验
    import React, { Component } from 'react'
    
    /**
     *
     * 防抖:防抖是指限制访问次数,在n秒内执行一次,但是如果在n秒内再次触发的话,会等待n秒再次执行
     * 节流:节流是指限制访问频率,是每n秒触发一次 
     * @param {*} fn 回调函数
     * @param {*} time 触发间隔
     */
    
    function debounce(fn, time) {
        let timer;
        return function () {
            const [x,y] = [...arguments];
            if (timer) {
                clearTimeout(timer)
            }
            timer =  setTimeout(fn.bind(this, x,y), time);  
        }
    }
    
    
    /**
     * 立即执行的防抖函数
     *
     * @param {*} fn 回调函数
     * @param {*} time 触发间隔
     * @returns
     */
    function rightNowDebounce(fn,time) {
        let timer;
        return function () {
            const [x,y] = [...arguments];
            if (timer) {
                clearTimeout(timer)
            } else {
                fn.apply(this, [x,y]);
            }
            timer =  setTimeout(fn.bind(this,x,y), time);  
        }
    }
    
    /**
     *
     * 时间差形式的防抖 (实现形式一)
     * @param {*} fn 回调函数
     * @param {*} durition 触发间隔
     * @returns
     */
    function durationThrottle(fn, durition) {
        let startTime;
        return function () {
            const [x,y] = [...arguments]
            if (startTime) {
                let cur = new Date().getTime();
                if (cur - startTime > durition) {
                    startTime = cur;
                    fn.apply(this, [x,y])
                }
            } else {
                startTime = new Date().getTime();
            }
        }
    }
    
    /**
     * 定时器形式的节流 (实现方式二)
     *
     * @param {*} fn 回调函数
     * @param {*} timer 触发间隔
     */
    function timerThrottle(fn, duration) {
        let timer
        return function () {
            const args = [...arguments]
            console.log('args', args)
            if (!timer) {
                timer = setTimeout(function() {
                    fn.apply(this, args);
                    timer = null;
                }.bind(this), duration);
            }
        }
    }
    
    /**
     *
     * 会立即执行的节流函数
     * @param {*} fn
     * @param {*} durition
     * @returns
     */
    
    function rightNowThrottle(fn, durition) {
        let startTime;
        return function () {
            const [x,y] = [...arguments]
            if (startTime) {
                let cur = new Date().getTime();
                if (cur - startTime > durition) {
                    startTime = cur;
                    fn.apply(this,[x,y])
                }
            } else {
                fn.apply(this,[x,y])
                startTime = new Date().getTime();
            }
        }
    }
    
    const fn1 = function () {
        const [x,y] = [...arguments]
        this.setState(function name({number}) {
            return {number: number+1, positionX:x, positionY:y} 
        })
    } 
    
    const definedDebounce = debounce(fn1, 1000)
    const definedRightNowDebounce = rightNowDebounce(fn1, 1000)
    const definedThrottle = durationThrottle(fn1, 1000)
    const definedRightNowThrottle = rightNowThrottle(fn1, 1000)
    const definedTimerThrottle = timerThrottle(fn1, 1000)
    
    export default class Debous extends Component {
        state = {
            number: 0,
            positionX: 0,
            positionY: 0,
        }
    
        hoverContaienr = (e) => {
            const { clientX, clientY } = e
             definedDebounce.apply(this, [clientX, clientY])
            // definedRightNowDebounce.apply(this, [clientX, clientY])
            // definedThrottle.apply(this, [clientX, clientY])
            // definedRightNowThrottle.apply(this, [clientX, clientY])
            // definedTimerThrottle.apply(this, [clientX, clientY])
    
            
        }
    
        render() {
            return (
                <div 
                    style={{  '100%', height:300, background:'pink', fontSize:'36px',textAlign:'center' }}
                    onMouseMove={this.hoverContaienr}
                >
                    <div style={{position:'absolute', top: '50%', left:'50%', transform:'translate(-50%, -50%)'}}>
                        {this.state.number}
                        <span style={{fontSize:16, color: '#333'}}>({`${this.state.positionX},${this.state.positionY}`})</span>
                    </div>
                </div>
            )
        }
    }
    
    

    参考文档 防抖节流

  • 相关阅读:
    【Azure 事件中心】使用Kafka消费Azure EventHub中数据,遇见消费慢的情况可以如何来调节呢?
    【Azure 媒体服务】记录使用Java调用Media Service API时候遇见的一些问题
    【Azure 应用服务】App Service 的.NET Version选择为.NET6,是否可以同时支持运行ASP.NET V4.8的应用呢?
    【Azure 应用服务】应用服务中发布Docker Container,如何添加卷(如Azure File Share)以便永久存储文件
    【Azure 应用服务】在App Service中调用外部服务API时需要携带客户端证书,而多次调用的情况下会出现WindowsCryptographicException Keyset does not exist异常
    【Azure 事件中心】在Azure Function App中消费Event Hub数据,时常出现EventReceiveError
    【Azure 环境】IntelliJ IDEA Community Edition 2021.2.3登陆Azure账号时,无法切换到中国区
    【Azure Redis 缓存】Azure Redis Cluster 在增加分片数时失败分析
    【Azure 应用服务】Storage Queue触发Azure Function时报错 The input is not a valid Base64 string
    【Azure API 管理】使用APIM进行XML内容读取时遇见的诡异错误 Expression evaluation failed. Object reference not set to an instance of an object.
  • 原文地址:https://www.cnblogs.com/wuhaozhou/p/14256172.html
Copyright © 2020-2023  润新知