当我们的回调函数(尤其是异步操作,比如请求数据)在短时间内,被连续调用时,会出现卡顿现象,此时的用户体验下降和性能消耗增大;
所以需要添加防抖函数来解决:在设定的事件间隔,n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时,只执行最后一次操作;这样可以减少对性能的消耗;
可以使用的插件比较多;比如underscore,lodash
lodash中文文档
https://www.lodashjs.com/underscore中文文档
https://www.underscore-js.com/
下面简单用计数器演示下,防抖的操作;
当在连续点击计数按钮时,不会连续触发计数;
- //css代码
- div {
- width: 500px;
- height: 500px;
- background-color: pink;
- font-size: 1.25rem;
- font-weight: 700;
- text-align: center;
- line-height: 500px;
- }
-
- <body>
- <div id="container">div>
- <button>点我button>
- <script src="./underscore/underscore.js">script>
- <script>
- let count = 0;
- let container = document.querySelector('#container');
- let button = document.querySelector('button')
-
- function countNum(e) {
- count++;
- container.innerHTML = count;
- }
- button.onclick = _.debounce(countNum, 300, true);
- script>
- body>
1.debounce函数的参数有三个:
debounce(func,wait,immidiate);
(1) func 表示事件执行的回调函数;
(2)wait 表示事件延迟执行的等待时间;
(3) immediate 表示的是是否立即执行,参数为true时,表示立即执行;为false时,表示在wait等待时间过后执行;
下面主要对三个参数来分析实现功能:
(1)延时功能使用 settimeout 来实现;
(2)this指向问题,在插件中,回调函数的this指向是指向事件的触发者的;所以在做封装时,需要使用call或者apply来改变this指向问题(不改变的化,this是指向window的),因为debounce函数是事件执行的回调函数,所以在debounce中,this指向是指向事件调用者的,所以使用context来保存this,在返回的函数中,使用call来修改func函数的指向;
(3)event事件的问题,在插件中的event参数是事件的事件的具体类型;在debounce函数中的arguments中是保存了事件调用的信息,所以将argument的事件,使用call方法将参数传递给func;
- function debounce(func, wait,immediate) {
- let timeout;
- return function () {
- //在wait时间内,重复触发事件的时候,清除定时器,重新进入wait时间
- // console.log(arguments);//arguments参数是具有事件对象的,所以直接将arguments参数传递给func函数
- let args = arguments[0];
- let context = this;
- // console.log(this);//-->指向调用者
- clearTimeout(timeout)
- //当immediate参数为true时,是立即执行,不进入延迟操作,即进入第一个if判断分支
- if (immediate) {
- let callNow = !timeout;
- timeout = setTimeout(() => {
- timeout = null;
- }, wait);
- //immediate参数设置为true时,(1)刚开始进来timeout还没有生成,即为undefined,取反为true;就会进入下面的立即调用func函数,即立即执行,延时函数开始计时,生成了一个timeout,所以在wait时间内,再次触发事件的时候,callnow不再是true(因为生成了一个timeout,不再是undefined,取反为false),当wait时间过后,重置了timeout = null,所以wait时间后,再次触发事件时,callNow为true(因为timeout置为null了)
- if(callNow)func.call(context,args)
- } else {
- //当immediate参数是false时,不进入延迟操作函数
- timeout = setTimeout(function () {
- //修改this指向,使用call方法
- func.call(context,args)
- }, wait);
- }
-
- }
- }