防抖与节流

Posted mhxy13867806343

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了防抖与节流相关的知识,希望对你有一定的参考价值。

  1 控制函数触发时间:
  2 
  3 function myWait(func, wait, options) {
  4 
  5     var context, args, result;
  6 
  7 
  8 
  9     // setTimeout 的 handler
 10 
 11     var timeout = null;
 12 
 13 
 14 
 15     // 标记时间戳
 16 
 17     // 上一次执行回调的时间戳
 18 
 19     var previous = 0;
 20 
 21 
 22 
 23     // 如果没有传入 options 参数
 24 
 25     // 则将 options 参数置为空对象
 26 
 27     if (!options)
 28 
 29       options = {};
 30 
 31 
 32 
 33      var later = function() {
 34 
 35        // 如果 options.leading === false
 36 
 37        // 则每次触发回调后将 previous 置为 0
 38 
 39        // 否则置为当前时间戳
 40 
 41        previous = options.leading === false ? 0 :new Date();
 42 
 43        timeout = null;
 44 
 45        result = func.apply(context, args);
 46 
 47        // 这里的 timeout 变量一定是 null 了吧
 48 
 49        // 是否没有必要进行判断?
 50 
 51        if (!timeout)
 52 
 53          context = args = null;
 54 
 55      };
 56 
 57  
 58 
 59      // 以滚轮事件为例(scroll)
 60 
 61      // 每次触发滚轮事件即执行这个返回的方法
 62 
 63      // _.throttle 方法返回的函数
 64 
 65      return function() {
 66 
 67        // 记录当前时间戳
 68 
 69        var now =new Date();
 70 
 71  
 72 
 73        // 第一次执行回调(此时 previous 为 0,之后 previous 值为上一次时间戳)
 74 
 75        // 并且如果程序设定第一个回调不是立即执行的(options.leading === false)
 76 
 77        // 则将 previous 值(表示上次执行的时间戳)设为 now 的时间戳(第一次触发时)
 78 
 79      // 表示刚执行过,这次就不用执行了
 80 
 81      if (!previous && options.leading === false)
 82 
 83        previous = now;
 84 
 85      // 距离下次触发 func 还需要等待的时间
 86 
 87        var remaining = wait - (now - previous);
 88 
 89        context = this;
 90 
 91        args = arguments;
 92 
 93  
 94 
 95        // 要么是到了间隔时间了,随即触发方法(remaining <= 0)
 96 
 97        // 要么是没有传入 {leading: false},且第一次触发回调,即立即触发
 98 
 99        // 此时 previous 为 0,wait - (now - previous) 也满足 <= 0
100 
101        // 之后便会把 previous 值迅速置为 now
102 
103      // ========= //
104 
105        // remaining > wait,表示客户端系统时间被调整过
106 
107        // 则马上执行 func 函数
108 
109        // @see https://blog.coding.net/blog/the-difference-between-throttle-and-debounce-in-underscorejs
110 
111        if (remaining <= 0 || remaining > wait) {
112 
113         if (timeout) {
114 
115            clearTimeout(timeout);
116 
117            // 解除引用,防止内存泄露
118 
119            timeout = null;
120 
121          }
122 
123  
124 
125          // 重置前一次触发的时间戳
126 
127          previous = now;
128 
129  
130 
131          // 触发方法
132 
133        // result 为该方法返回值
134 
135        result = func.apply(context, args);
136 
137        // 引用置为空,防止内存泄露
138 
139        // 感觉这里的 timeout 肯定是 null 啊?这个 if 判断没必要吧?
140 
141        if (!timeout)
142 
143          context = args = null;
144 
145      } else if (!timeout && options.trailing !== false) { // 最后一次需要触发的情况
146 
147        // 如果已经存在一个定时器,则不会进入该 if 分支
148 
149      // 如果 {trailing: false},即最后一次不需要触发了,也不会进入这个分支
150 
151          // 间隔 remaining milliseconds 后触发 later 方法
152 
153          timeout = setTimeout(later, remaining);
154 
155        }
156 
157  
158 
159        // 回调返回值
160 
161        return result;
162 
163      };
164 
165 };
166 
167 
168 
169 控制触发频率:
170 
171 function mybounce(func, wait, immediate) {
172 
173      var timeout, args, context, timestamp, result;
174 
175  
176 
177      var later = function() {
178 
179        // 定时器设置的回调 later 方法的触发时间,和连续事件触发的最后一次时间戳的间隔
180 
181        // 如果间隔为 wait(或者刚好大于 wait),则触发事件
182 
183        var last = new Date() - timestamp;
184 
185  
186 
187        // 时间间隔 last 在 [0, wait) 中
188 
189        // 还没到触发的点,则继续设置定时器
190 
191        // last 值应该不会小于 0 吧?
192 
193        if (last < wait && last >= 0) {
194 
195          timeout = setTimeout(later, wait - last);
196 
197        } else {
198 
199          // 到了可以触发的时间点
200 
201          timeout = null;
202 
203          // 可以触发了
204 
205          // 并且不是设置为立即触发的
206 
207          // 因为如果是立即触发(callNow),也会进入这个回调中
208 
209          // 主要是为了将 timeout 值置为空,使之不影响下次连续事件的触发
210 
211          // 如果不是立即执行,随即执行 func 方法
212 
213          if (!immediate) {
214 
215            // 执行 func 函数
216 
217            result = func.apply(context, args);
218 
219            // 这里的 timeout 一定是 null 了吧
220 
221            // 感觉这个判断多余了
222 
223            if (!timeout)
224 
225            context = args = null;
226 
227          }
228 
229        }
230 
231      };
232 
233  
234 
235      // 嗯,闭包返回的函数,是可以传入参数的
236 
237      // 也是 DOM 事件所触发的回调函数
238 
239      return function() {
240 
241        // 可以指定 this 指向
242 
243        context = this;
244 
245        args = arguments;
246 
247  
248 
249        // 每次触发函数,更新时间戳
250 
251        // later 方法中取 last 值时用到该变量
252 
253        // 判断距离上次触发事件是否已经过了 wait seconds 了
254 
255        // 即我们需要距离最后一次事件触发 wait seconds 后触发这个回调方法
256 
257        timestamp = new Date();
258 
259  
260 
261        // 立即触发需要满足两个条件
262 
263        // immediate 参数为 true,并且 timeout 还没设置
264 
265        // immediate 参数为 true 是显而易见的
266 
267        // 如果去掉 !timeout 的条件,就会一直触发,而不是触发一次
268 
269        // 因为第一次触发后已经设置了 timeout,所以根据 timeout 是否为空可以判断是否是首次触发
270 
271        var callNow = immediate && !timeout;
272 
273  
274 
275        // 设置 wait seconds 后触发 later 方法
276 
277       // 无论是否 callNow(如果是 callNow,也进入 later 方法,去 later 方法中判断是否执行相应回调函数)
278 
279        // 在某一段的连续触发中,只会在第一次触发时进入这个 if 分支中
280 
281        if (!timeout)
282 
283          // 设置了 timeout,所以以后不会进入这个 if 分支了
284 
285        timeout = setTimeout(later, wait);
286 
287      // 如果是立即触发
288 
289      if (callNow) {
290 
291        // func 可能是有返回值的
292 
293        result = func.apply(context, args);
294 
295        // 解除引用
296 
297        context = args = null;
298 
299      }
300 
301        return result;
302 
303      };
304 
305 };
306 
307 
308 
309 示例:
310 
311 var x=0;
312 
313 function add(){
314 
315     x++
316 
317     $(‘p‘).html(x)
318 
319 }
320 
321 $(‘.div‘).mouseenter(myWait(add,1000))

 

以上是关于防抖与节流的主要内容,如果未能解决你的问题,请参考以下文章

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

js 防抖与节流原理

JavaScript防抖与节流

JavaScript防抖与节流

面试必问题:JS防抖与节流

防抖与节流区别以及使用场景