• 技术文章 >web前端 >js教程

    JavaScript专题之三:防抖

    coldplay.xixicoldplay.xixi2021-03-04 09:56:00转载896

    目录

    (相关免费学习推荐:javascript视频教程

    一、为什么需要防抖

    为此,我们举个示例代码来了解事件如何频繁的触发:

    我们写一个 index.html 文件:

    <html lang="en"><head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <title>debounce</title>
        <style>
            #wrapper {
                width: 100%;
                height: 200px;
                line-height: 200px;
                text-align: center;
                color: #fff;
                background-color: #444;
                font-size: 30px;
            }
        </style></head><body>
        <p id="wrapper"></p>
        <script>
            var count = 1;
            var op = document.getElementById("wrapper");
    
            function getUserAction() {
                op.innerHTML = count++;
            }
    
            op.onmousemove = getUserAction;
        </script></body></html>

    从左边滑到右边就触发了近100次getUserAction 函数!看如下Gif:

    在这里插入图片描述

    因为这个例子很简单,所以浏览器完全反应的过来,但假设:

    这种在一瞬间(短时间内)对浏览器或服务器造成了过多压力的交互就需要进行优化了,为了解决这个问题,一般有两种解决方案:

    他们的目的都是:降低一个函数的触发频率,以提高性能或避免资源浪费。

    二、防抖的原理

    今天重点讲讲防抖的实现。

    防抖的原理就是:你尽管触发事件,但是我一定在事件触发n秒无操作后才执行。举个例子:

    我们规定3s为防抖的标准,那么:

    1. 第一次要求执行事件 - 此时倒计时3s
    2. 倒计时2s
    3. 倒计时1s
    4. 0.5s时事件再次被触发 - 此时倒计时3s
    5. …3s内无事发生
    6. 执行事件,共用了5.5s

    三、自己实现一个防抖

    3.1 第一版

    我们根据上一节提到的核心思想,实现第一版代码:

    function debounce(func, wait) {
        var timer;
        return function () {
            clearTimeout(timer)
            timer = setTimeout(func, wait);
        }}

    如果我们要使用它,第一节的例子为例:

    op.onmousemove = debounce(getUserAction, 2000);

    此时大家可以再次测试一下,事件持续发生时,只有在完全停止2s后,才会触发事件:

    写到这里,作为针对部分高频事件的需求来说,已经结束了。我们来看看他的效果:

    在这里插入图片描述

    3.2 第二版

    大家都知道,dom节点在触发事件的时候,this指向它本身,本例中则指向op,但是在本例中:我们看一下

    var count = 1;var op = document.getElementById("op");function getUserAction() {
        op.innerHTML = count++;
        console.log('this', this); // 此时输出 Window...}op.onmousemove = debounce(getUserAction, 2000);function debounce(func, wait) {
        var timer;
        return function () {
            clearTimeout(timer)
            timer = setTimeout(func, wait);
        }}

    毕竟经过了一层匿名函数的包裹,this已经指向了window,为了减少影响,我们尝试修正它

    function debounce(func, wait) {
        var timer;
        return function () {
            var _this = this; // 记录当前this
    
            clearTimeout(timer)
            timer = setTimeout(function(){
                func.apply(_this); //将 func的this改为_this
            }, wait);
        }}

    第三版

    解决的this指向问题,我们的函数仍然不够“完美”,JavaScript中,事件处理函数会提供event对象,我们简称为e。

    // 使用了 debouce 函数function getUserAction(e) {
        console.log(e); // undefined
        op.innerHTML = count++;};

    为了保证它的原汁原味,我们再改第三版:

    var count = 1;var op = document.getElementById("op");function getUserAction(e) {
        op.innerHTML = count++;
        console.log('e', e); // MouseEvent}op.onmousemove = debounce(getUserAction, 2000);function debounce(func, wait) {
        var timer;
        return function () {
            var _this = this; // 记录当前this
            var arg = arguments; // 记录参数
            clearTimeout(timer)
            timer = setTimeout(function () {
                func.apply(_this, arg); //将 func的this改为_this
            }, wait);
        }}

    到此为止,我们在尽可能保留Dom事件原有能力的情况下,给函数加上了防抖效果,它可以解决大部分我们日常开发的防抖问题,但我们需要更“完美”

    四、防抖进阶

    4.1 立即执行

    这个需求就是:

    想想这个需求也是很有道理的嘛,那我们加个immediate参数判断是否是立刻执行。

    function debounce(func, wait, immediate) {
        var timer;
    
        return function () {
            var _this = this;
            var args = arguments;
    
            if (timer) clearTimeout(timer); // 常规流程,间隔内触发时清掉重置定时
            if (immediate) {
                // 如果已经执行过,不再执行
                var callNow = !timer; // 1. callNow 初始值是 true, 同步立即执行;随后 timer 才开始执行
                timer = setTimeout(function(){
                    timer = null; // wait 期间,timer 是一个 ID 数字,所以 callNow 为 false,func 在此期间永远不会执行
                }, wait) // wait 之后,timer 赋值 null,callNow 为 true,func 又开始立即执行。
                if (callNow) func.apply(_this, args)
            }
            else {
                timer = setTimeout(function(){
                    func.apply(_this, args)
                }, wait);
            }
        }}

    再来看下此时他是什么效果:

    在这里插入图片描述

    4.2 添加简单验证

    function debounce(func, wait, immediate) {
        var timer;
        // 检查函数
        if (typeof func !== 'function') {
            throw new TypeError('Expected a function');
        }
        // 保证wait存在
        wait = +wait || 0;
    
        const debounced = function () {
            var _this = this;
            var args = arguments;
    
            if (timer) clearTimeout(timer); // 常规流程,间隔内触发时清掉重置定时
            if (immediate) {
                // 如果已经执行过,不再执行
                var callNow = !timer; // 如果不存在定时器,则callNow为true
                timer = setTimeout(function () {
                    timer = null; // 为了保证之后的时效性,手动添加timer
                }, wait)
                // 因为不存在timer,证明是首次执行,所以直接调用
                if (callNow) func.apply(_this, args)
            }
            else {
                timer = setTimeout(function () {
                    func.apply(_this, args)
                }, wait);
            }
        }
        return debounced}

    4.3 添加取消事件方法

    如果你希望能取消被防抖的事件,我们可以这样写:

    function debounce(func, wait, immediate) {
        var timer;
        // 检查函数
        if (typeof func !== 'function') {
            throw new TypeError('Expected a function');
        }
        // 保证wait存在
        wait = +wait || 0;
    
        const debounced = function () {
            var _this = this;
            var args = arguments;
    
            if (timer) clearTimeout(timer); // 常规流程,间隔内触发时清掉重置定时
            if (immediate) {
                // 如果已经执行过,不再执行
                var callNow = !timer; // 如果不存在定时器,则callNow为true
                timer = setTimeout(function () {
                    timer = null; // 为了保证之后的时效性,手动添加timer
                }, wait)
                // 因为不存在timer,证明是首次执行,所以直接调用
                if (callNow) func.apply(_this, args)
            }
            else {
                timer = setTimeout(function () {
                    func.apply(_this, args)
                }, wait);
            }
        }
    
        const cancel = function(){
            clearTimeout(timer);
            timer = null;
        }
    
        const pending = function(){
            return timer !== undefined;
        }
    
        debounced.cancel = cancel;
        debounced.pending = pending;
        return debounced}

    我们再来看看效果:

    在这里插入图片描述

    写到这里这个简单的防抖方法就算OK了,它确实还不算完美,如果在改进上有任何建议,不妨在评论区留言吧~

    相关免费学习推荐:javascript(视频)

    以上就是JavaScript专题之三:防抖的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:CSDN,如有侵犯,请联系admin@php.cn删除
    专题推荐:JavaScript 防抖
    上一篇:怎么降低node版本 下一篇:浅谈Angular中http请求模块的用法
    大前端线上培训班

    相关文章推荐

    • JavaScript中防抖节流的详细介绍(代码示例)• js如何实现函数防抖与节流• 轻松理解函数防抖和节流的使用• Vue中怎么对事件进行防抖和节流操作?• premiere防抖动效果在哪里

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网