JS事件中防抖debounce和节流throttle以及requestAnimationFrame

浏览器的一些事件,如:resize,scroll,keydown,keyup,keypress,mousemove等。这些事件触发频率太过频繁,绑定在这些事件上的回调函数会不停的被调用。这样浏览器的目的是为了保证信息的一致性,而对于我们来说就是一种资源的浪费了。
debounce的作用是在让在用户动作停止后延迟x ms再执行回调。
throttle的作用是在用户动作时没隔一定时间(如200ms)执行一次回调。
他们两个的共同点就是将多次回调的触发合并成一次执行。这就大大避免了过于频繁的事件回调操作。
本质就是将事件回调函数用debounce或throttle包装,事件触发的频率没有改变,只是我们自定义的回调函数的执行频率变低了。这个处理是基于DOM操作是十分巨大的开销。所以如果你的回调函数只是处理一些js的数据,那么用不用防抖和节流处理是一样的。

debounce实现

function debounce(fn,delay){

    var delay=delay||200;
    var timer;
    return function(){
        var th=this;
        var args=arguments;
        if (timer) {
            clearTimeout(timer);
        }
        timer=setTimeout(function () {
                timer=null;
                fn.apply(th,args);
        }, delay);
    };
}
例子1

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Documenttitle>
head>
<style type="text/css">
    #dom{
        width: 200px; height: 200px; background: red
    }
style>
<body>
    <div id="dom">div>
body>
<script type="text/javascript">
document.getElementById('dom').addEventListener("mousemove",debounce(function(){
    console.log(this)
}));
script>
html>
例子2

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Documenttitle>
head>
<body>
    <div class="keycont">
        输入:<input type="text" id="keybtn"/>
    div>
    <div style="position:relative">
        <div class="keysend" id="keyinfo">div>
    div>
body>
<script type="text/javascript">
    var info=document.getElementById("keyinfo");
    var btn=document.getElementById("keybtn");
    btn.addEventListener("keydown",debounce(function(){
        var div=document.createElement("div");
        var str=new Date();
        div.innerText=str
        info.appendChild(div);
    },500));
script>
html>

throttle实现

function throttle(fn,interval){
    var last;
    var timer;
    var interval=interval||200;
    return function(){
        var th=this;
        var args=arguments;
        var now=+new Date();
        if(last&&now-lastfunction(){
                last=now;
                fn.apply(th,args);
            },interval);
        }else{
            last=now;
            fn.apply(th,args);
        }
    }
}
window.addEventListener('scroll', throttle(function  () {
   console.log(11111) 
}));

使用方法跟debounce一样。代码逻辑也类似。在触发时超过间隔时间interval ms则执行。否则不执行。if判断中的setTimeout是保证最后一次事件触发后能够调用,所以每次执行没到间隔时间时先清除timer,再重新启动timer。而在达到间隔时间时执行函数。代码逻辑也很简单,不用多说,相信聪明的你一看就能明白。

这个throttle节流的功能就是在固定的间隔时间执行回调函数,最常用的用处就是resize,scroll事件中处理。

requestAnimationFrame

上面介绍的抖动与节流实现的方式都是借助了定时器 setTimeout ,但是如果页面只需要兼容高版本浏览器或应用在移动端,又或者页面需要追求高精度的效果,那么可以使用浏览器的原生方法 requestAnimationFrame
window.requestAnimationFrame() 这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数。这个方法接受一个函数为参,该函数会在重绘前调用。常用于 web 动画的制作,用于准确控制页面的帧刷新渲染,让动画效果更加流畅,当然它的作用不仅仅局限于动画制作,我们可以利用它的特性将它视为一个定时器。(当然它不是定时器)

通常来说,requestAnimationFrame 被调用的频率是每秒 60 次,也就是 1000/60 ,触发频率大概是 16.7ms 。(当执行复杂操作时,当它发现无法维持 60fps 的频率时,它会把频率降低到 30fps 来保持帧数的稳定。)

简单而言,使用 requestAnimationFrame 来触发滚动事件,相当于上面的:

throttle(fn,16.7)

总结

debounce用在keydown事件上验证用户名最好。而throttle用在resize改变布局上,onscroll滚动时候的。 requestAnimationFrame 可调节性十分差。但是相比 throttle(func, 16.7) ,用于更复杂的场景时,requestAnimationFrame 可能效果更佳,性能更好。

你可能感兴趣的:(JavaScript,性能优化,web前端)