js防抖具体实现以及详细原理步骤说明(附实例)

 更新时间:2022年09月01日 11:27:08   作者:指定能行  
节流和防抖这里两个词可能对一些初入JavaScript的同学比较陌生,下面这篇文章主要给大家介绍了关于js防抖具体实现以及详细原理步骤的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

ps:本文将从一个案例出发循序渐进,在其中你不仅能知道防抖是如何实现的,还可以学习到关于 this 、apply、arguments 等知识的具体应用。

Why?为啥要有防抖?

因为有时候频繁的事件触发是没有意义的,不仅影响性能还可能造成卡顿。

为了避免这种情况,我们需要用防抖来解决这个问题。

What? 啥是防抖?

防抖debounce:

简单来说,防抖的效果就是在一定的时间间隔内,多次触发只有一次触发产生

How? 防抖咋用啊?

首先先从一个实例入手:

我们设置一个按钮用来切换上方文本的颜色,如下:

    <h2 id="demo">hello</h2>
    <button id="btn">点我</button>
 
    <script type="text/javascript">
        var btn = document.getElementById('btn');
 
        btn.addEventListener('click', changeColor, false);
        
        var flag = true
        function changeColor() {
            if (flag) {
                document.getElementById('demo').style.color = 'pink'
            } else {
                document.getElementById('demo').style.color = 'blue'
            }
            flag = !flag
        }

如果你想要让用户在1秒内不能频繁切换 ,即1秒内只能给文本换一种颜色。

那么你可以利用定时器来实现,不过直接写一个定时器是不够的,因为你只能实现多次触发延时执行,而不是限制触发。

你可以使用一个变量来判断你的每次按下按钮时候是否已经触发过定时器了,如果触发过了就将原来触发但还没到1秒的定时器清除,接着重新来个1秒的定时器;如果没触发过说明你1秒内没按过,新建一个1秒的定时器就行。

这样就可以保证防抖的效果实现了,请看代码:

    <h2 id="demo">hello</h2>
    <button id="btn">点我</button>
 
    <script type="text/javascript">
        var btn = document.getElementById('btn');
 
        btn.addEventListener('click', debounce(changeColor), false);
 
        var flag = true
        function changeColor() {
            if (flag) {
                document.getElementById('demo').style.color = 'pink'
            } else {
                document.getElementById('demo').style.color = 'blue'
            }
            flag = !flag
        }
 
        function debounce(fn) {
            let t = null
            return function () {
                //如果定时器存在就清除掉
                if (t) {
                    clearTimeout(t)
                }
                //不然就创建新的定时器
                t = setTimeout(function() {
                    fn()
                }, 1000)
            }
        }
</script>

 看完代码你可能会有如下疑惑:

1. 怎么这个debounce函数里面不能直接写执行内容吗?非要return一个函数?

解:因为你已经把changeColor函数当成参数传给debounce函数了(看下面的代码变化),你要在btn上绑定debounce事件就要在debounce里面将改造好的changeColor事件写成一个回调函数。

// btn.addEventListener('click', changeColor, false);
btn.addEventListener('click', debounce(changeColor), false);

2. 这个 let t = null; 岂不是会每次都将 t 置为 null ??这可咋实现呢?

 解:你以为 btn 绑定的是 debounce,所以你认为每次触发都会执行 t = null。

漏! btn 绑定的是debounce(changeColor)!这个括号不可忽视,实际上每次触发click事件执行的是它的回调,也就是 return 里面的内容,let t = null 只会在初始化的时候执行一次。

虽然目前已经实现了防抖的效果,但是这么写的话,你会发现changeColor函数是拿不到事件对象的,也就是说它拿不到本该属于它的 event 。

要想让它拿回本该属于它的 event ,你可以这么做: 

1.基于当前 event 已经在 debounce 上了,你可以将 e 当参数传递给 debounce 里回调的函数

2.再把e传给回调函数中定时器里的 fn() ,即 fn(e)

function debounce(fn) {
            let t = null
            //往这里传e
            return function (e) {
                if (t) {
                    clearTimeout(t)
                }
 
                t = setTimeout(function() {
                    //再带给fn
                    fn(e)
                }, 1000)
            }
        }

届时,你就会发现 changeColor 它拿到了事件对象! 

 如果你问,非要拿这个e干啥呢??

那么我只能说,拿到了e可以对e做一些操作(像是e.target可以更改等等),如果你不对e做什么不传给它也没关系,只是这么写可以离一个完美的防抖函数更近而已。

但是你不能保证只有一个参数需要传给 changeColor ,所以在传参的时候只写一个 e 没办法实现多个参数的传递,那么为了更完美一点,我们接着来改改。

说到多个参数的传递你大概会想到实参列表 arguments 。没错!就是它!

如果你不了解 arguments,请前往:学arguments去喽

 arguments对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引0处

首先,我们试着用 arguments[0] 去代替刚刚传递的 e ,你会发现:

 那个return里的 arguments[0] 根本传不到定时器里,那就更别提传到 changeColor 了。

因为每个函数里的 arguments 都是自己函数内部的,定时器里的函数没有 arguments 所以 undefined。

要解决这个问题的话,你可以使用赋值的方式把 arguments 存下来,还可以使用箭头函数。

接下来采用箭头函数的方式来解决:

因为箭头函数内部没有 arguments 对象,它会往外找,这样就可以得到 return 里的 arguments。

function debounce(fn) {
            let t = null
            return function () {
                console.log('我是回调的arguments',arguments[0]);
                if (t) {
                    clearTimeout(t)
                }
                
                //这里用箭头函数
                t = setTimeout(() => {
                    fn(arguments[0])
                    console.log('我是定时器里的arguments', arguments[0])
                    // console.log(this)
                }, 1000)
            }
        }

 这样子就实现了 arguments[0] 的传递问题,如果是多个参数的话可以使用 apply 方法 将整个 arguments 作为参数传递过去,如下:

function debounce(fn) {
            let t = null
            return function () {
                if (t) {
                    clearTimeout(t)
                }
 
                t = setTimeout(() => {
                    fn.apply(this, arguments)                    
                }, 1000)
            }
        }

其中,apply方法的第一个参数还可以将 changeColor 的 this 由 Window 转为 btn,属于是一个一举两得的大动作了。 (因为箭头函数会往外找this继承,所以拿到了return里的this再传给changeColor)

如果你不了解apply,请前往:学apply去喽

写到这里,一个 延迟debounce 就诞生了!

什么是延迟debounce??

顾名思义,在延迟结束那一刻才触发回调。

如果你觉得每次按完按钮还要等等才能改颜色真是太烦了,估计没等到改颜色你就关闭网页了。

前缘debounce 可以解决这个问题!(即在定时器开始的那一刻就触发事件)

将代码再改一改你就可以得到 前缘debounce 啦!

function debounce(fn) {
            let t = null
            return function () {
                // 用firstClick来记录每一次定时器开始的第一次按下的动作
                var firstClick = !t
 
                if(t) {
                    clearTimeout(t)
                }
 
                if(firstClick) {
                    fn.apply(this, arguments)
                }
 
                //等待1秒后将 t 置为 null,在这1秒内如果再点击事件就会去到if(t)的执行
                t = setTimeout(() => {
                    t = null
                }, 1000)
            }
        }

这个前缘debounce将会实现每次连续点击后先响应一次事件,再去等1秒。

总结

到此这篇关于js防抖具体实现以及详细原理步骤的文章就介绍到这了,更多相关js防抖实现内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • js取得html iframe中的元素和变量值

    js取得html iframe中的元素和变量值

    想要取得iframe中的元素和js变量值,不能用$(document).ready()方法,而是要用$("#iframeId").load()方法
    2014-06-06
  • JS动态插入脚本和插入引用外部链接脚本的方法

    JS动态插入脚本和插入引用外部链接脚本的方法

    js 动态插入脚本的是在页面加载时不存在,但将来的某一时刻通过修改该 DOM 动态添加的脚本。接下来通过本文给大家介绍JS动态插入脚本和插入引用外部链接脚本,需要的朋友可以参考下
    2018-05-05
  • ng-options和ng-checked在表单中的高级运用(推荐)

    ng-options和ng-checked在表单中的高级运用(推荐)

    AngularJS是当前非常的流行的前端框架,它的语法糖非常多,也极大的方便了前端开发者。这篇文章主要介绍了ng-options和ng-checked在表单中的高级运用,需要的朋友可以参考下
    2017-01-01
  • JavaScript正则表达式校验与递归函数实际应用实例解析

    JavaScript正则表达式校验与递归函数实际应用实例解析

    这篇文章主要介绍了JavaScript正则表达式校验与递归函数实际应用,需要的朋友可以参考下
    2017-08-08
  • 微信小程序开发之实现记账本

    微信小程序开发之实现记账本

    这篇文章主要为大家详细介绍了如何通过微信小程序开发一个简单的记账本,文中的示例代码讲解详细,感兴趣的小伙伴可以和小编一起学习一下
    2023-01-01
  • 教你使用webpack打包编译TypeScript代码

    教你使用webpack打包编译TypeScript代码

    TypeScript同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TypeScript,本文分步骤给大家介绍的非常详细,需要的朋友参考下吧
    2021-06-06
  • webpack模块化的原理解析

    webpack模块化的原理解析

    webpack 中实现模块化的核心就是 __webpack_require__ 函数,无论是commonjs模块化还是es 模块都是通过该函数来导入的,这篇文章主要介绍了webpack模块化的原理,需要的朋友可以参考下
    2023-02-02
  • js实现表格数据搜索

    js实现表格数据搜索

    这篇文章主要为大家详细介绍了js实现表格数据搜索,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • 利用JS响应式修改vue实现页面的input值

    利用JS响应式修改vue实现页面的input值

    这篇文章主要给大家介绍了关于如何利用JS响应式修改vue实现页面的input值,文中通过示例代码介绍的非常详细,对大家学习或者使用JS具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • 使用JavaScript库还是自己写代码?

    使用JavaScript库还是自己写代码?

    有时候在写JavaScript添加到你的网页中的时候,你将需要决定是使用已有的可用的JavaScript库还是自己写所有代码。其中每个各有优缺点,因此没有任何一种方式对每个人来说都是绝对正确的选择。
    2010-01-01

最新评论

?


http://www.vxiaotou.com