JS进阶篇:函数防抖和节流


throttle(节流):在规定时间内,保证执行/触发一次该函数。原理是通过判断是否到达一定时间来触发函数。

debounce(防抖):将几次操作合并为一此操作进行。当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始计时。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。

区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。

比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。

通俗的来讲

我们在进行窗口的resizescroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。

此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果。

防抖

定义:将几次操作合并为一此操作进行

在前端中有一些事件会频繁的触发容易造成页面卡顿

例如:

  1. window 的 resize、scroll
  2. mousedown、mousemove
  3. keyup、keydown
  4. input 的 input 事件

搜索框搜索输入,只需用户最后一次输入完,再发送请求;

用户名、手机号、邮箱输入验证;

浏览器窗口大小改变后,只需窗口调整完后,再执行 resize 事件中的代码,防止重复渲染。

立即执行版:

//防抖(立即执行)
function debounce(fn,wait = 500){
    let timerId;
    let flag = true;
    return function(){
        clearTimeout(timerId);
        if(flag){
            fn.apply(this,arguments);
            flag = false;
        }else{
            timerId = setTimeout(function(){
                flag = true;
            },wait);  
        }
    }
}    

非立即执行版:

//防抖(非立即执行)
function debounce(fn,wait = 500){
    let timerId;
    return function(){
        if(timerId){
            clearTimeout(timerId)
        }
        timerId = setTimeout(() => {
            fn.apply(this,arguments)
        },wait)
    }  
}

合并版:

//防抖(合并版)
function debounce_merge(fn,wait = 500,isImmediate = false){
    let timerId = null;
    let flag = true;
    if(isImmediate){
        return function(){
            clearTimeout(timerId);
            if(flag){
                fn.apply(this,arguments);
                flag = false
            }else{
                timerId = setTimeout(function(){
                    flag = true;
                },wait);  
            }
        }
    }else{
        return function(){
            if(timerId){
                clearTimeout(timerId)
            }
            timerId = setTimeout(() => {
                fn.apply(this,arguments)
            },wait)
        }
    }
}

节流

定义:让事件在一定时间内只执行一次

实际运用:搜索框input事件,例如要支持输入实时搜索可以使用节流方案。

节流throttle代码(时间戳):

function throttle(fn,wait = 500){
    let startTime = 0;
    return function(){
        let endTime = Date.now();
        if(endTime-startTime>wait){
            fn.apply(this,arguments);
            startTime = endTime;
        }
    }
}
// var throttle = function(fn, delay) {            
//   var prev = Date.now();            
//   return function() {                
//     var context = this;                
//     var args = arguments;                
//     var now = Date.now();                
//     if (now - prev >= delay) {                    
//       fn.apply(context, args);                    
//       prev = Date.now();                
//     }            
//   }        
// }        
// function handle() {            
//   console.log(Math.random());        
// }        
// window.addEventListener('scroll', throttle(handle, 1000));

节流throttle代码(定时器):

function throttle(fn,wait){
    let timeId = null;
    return function(){
        if(!timeId){
            timeId = setTimeout(function(){
                fn.apply(this,arguments);
                timeId = null;
            },wait)
        }
    }
}
// // 节流throttle代码(定时器):
// var throttle = function(fn, delay) {            
//     var timer = null;            
//     return function() {                
//         var context = this;               
//         var args = arguments;                
//         if (!timer) {                    
//             timer = setTimeout(function() {                        
//                 fn.apply(context, args);                        
//                 timer = null;                    
//             }, delay);                
//         }            
//     }        
// }        
// function handle() {            
//     console.log(Math.random());        
// }        
// window.addEventListener('scroll', throttle(handle, 1000));

节流throttle代码(时间戳+定时器):

// 节流throttle代码(时间戳+定时器):
var throttle = function(fn, delay) {     
    var timer = null;     
    var startTime = Date.now();     
    return function() {             
        var curTime = Date.now();             
        var remaining = delay - (curTime - startTime);             
        var context = this;             
        var args = arguments;             
        clearTimeout(timer);              
        if (remaining <= 0) {                    
            fn.apply(context, args);                    
            startTime = Date.now();              
        } else {                    
            timer = setTimeout(fn, remaining);              
        }      
    }
}
function handle() {      
    console.log(Math.random());
} 
window.addEventListener('scroll', throttle(handle, 1000));

以上都是非立即执行版本

//节流(立即执行)
function throttle(fn,wait = 500){
    let timeId = null;
    return function(){
        if(!timeId){
            timeId = true;
            fn.call(this, ...arguments);
            timeId = setTimeout(function(){
                timeId = null;
            },wait)
        }
    }
}
//节流(合并)
function throttle_merge(fn,wait = 500,isImmediate = false){
    let flag = true;
    let timer = null;

    if(isImmediate){
        return function(){
            if(flag) {
                fn.apply(this,arguments);
                flag = false;
                timer = setTimeout(() => {
                    flag = true
                },wait)
            }
        }
    }else{
        return function(){
            if(flag == true){
                flag = false;
                var timer = setTimeout(() => {
                    fn.apply(this,arguments)
                    flag = true
                },wait)
            }
        }
    }
}

文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录