浏览器的 resize,scroll,keypress,mousemove 等事件在触发时, 会不断地调用绑定在事件上的回调函数, 极大地浪费资源, 降低前端性能. 为了优化体验, 需要对这类事件进行调用次数的限制.
防抖 (debounce)
作用是在短时间内多次触发同一个函数, 只执行最后一次, 或者只在开始时执行.
以用户拖拽改变窗口大小, 触发 resize 事件为例, 在这过程中窗口的大小一直在改变, 所以如果我们在 resize 事件中绑定函数, 这个函数将会一直触发, 而这种情况大多数情况下是无意义的, 还会造成资源的大量浪费.
这时候可以使用函数防抖来优化相关操作:
- // 普通方案
- window.addEventListener('resize', () => {
- console.log('trigger');
- })
优化方案:
- // debounce 函数接受一个函数和延迟执行的时间作为参数
- function debounce(fn, delay){
- // 维护一个 timer
- let timer = null;
- return function() {
- // 获取函数的作用域和变量
- let context = this;
- let args = arguments;
- clearTimeout(timer);
- timer = setTimeout(function(){
- fn.apply(context, args);
- }, delay)
- }
- }
- function foo() {
- console.log('trigger');
- }
- // 在 debounce 中包装我们的函数, 过 2 秒触发一次
- window.addEventListener('resize', debounce(foo, 2000));
在 resize 事件上绑定处理函数, 这时 debounce 函数会立即调用, 实际上绑定的函数时 debounce 函数内部返回的函数.
每一次事件被触发, 都会清除当前的 timer 然后重新设置超时调用.
只有在最后一次触发事件, 才能在 delay 时间后执行.
我们也可以为 debounce 函数加一个参数, 可以选择是否立即执行函数
- function debounce(func, delay, immediate){
- var timer = null;
- return function(){
- var context = this;
- var args = arguments;
- if(timer) clearTimeout(timer);
- if(immediate){
- var doNow = !timer;
- timer = setTimeout(function(){
- timer = null;
- },delay);
- if(doNow){
- func.apply(context,args);
- }
- }else{
- timer = setTimeout(function(){
- func.apply(context,args);
- },delay);
- }
- }
- }
节流 (throttle)
类似于防抖, 节流是在一段时间内只允许函数执行一次.
应用场景如: 输入框的联想, 可以限定用户在输入时, 只在每两秒钟响应一次联想.
可通过时间戳和定时器来实现.
时间戳实现:
- var throttle = function(func, delay){
- var prev = Date.now();
- return function(){
- var context = this;
- var args = arguments;
- var now = Date.now();
- if(now-prev>=delay){
- func.apply(context,args);
- prev = Date.now();
- }
- }
- }
定时器实现:
- var throttle = function(func, delay){
- var timer = null;
- return function(){
- var context = this;
- var args = arguments;
- if(!timer){
- timer = setTimeout(function(){
- func.apply(context, args);
- timer = null;
- },delay);
- }
- }
- }
区别在于, 使用时间戳实现的节流函数会在第一次触发事件时立即执行, 以后每过 delay 秒之后才执行一次, 并且最后一次触发事件不会被执行; 而定时器实现的节流函数在第一次触发时不会执行, 而是在 delay 秒之后才执行, 当最后一次停止触发后, 还会再执行一次函数.
来源: https://juejin.im/entry/5ad74f065188252ead091937