性能一直是前端的共同话题。其中一个性能问题就是我们会频繁触发一些事件,比如mousemove、scroll、resize等,虽然浏览器控制器已经对这些事件的触发做了一些优化,但是如果在短时间内频繁触发还是影响性能,这个时候就需要今天的主角:防抖和节流,用它们来优化和提升性能。1防抖1.1定义防抖是将多个高频操作优化为只在最后一次执行(一个函数无论在一定时间内触发多少次回调,都只会在最后一次执行).通常的使用场景是:用户输入,输入完成后只需要做一次输入验证。1.2实现防抖就是将多个操作合并为一个操作。原理是维护一个定时器,在指定时间后触发函数,但如果在指定时间内再次触发,则之前的定时器会被取消并重新设置,从而确保只能触发最后一个操作。实现步骤如下:用闭包保存一个定时器变量,然后返回一个函数(返回的函数就是后面频繁触发的操作中调用的函数);第一次根据flag判断是否需要立即执行(因为有些情况需要在第一次调用函数的时候立即执行,如果没有这个参数,会在function之后执行)计时器到期);当有新的触发器时,如果有定时器,定时器会被清除;设置一个新的定时器来重新启动定时器。functiondebounce(fn,wait,immediate){lettimer=null;returnfunction(...args){//立即执行的函数(timer为空表示第一次触发)if(immediate&&!timer){fn.apply(this,args);}//如果有新的trigger,清空定时器timer&&clearTimeout(timer);//重新启动timer=setTimeout(()=>{fn.apply(this,args);},wait);}}1.3效果以上理论知识可以通过预览和观察效果图来验证:防抖后,输出内容的频率降低;防抖后,会在一定时间后输出内容。2节流2.1定义节流是在一定时间后执行,即降低频率,将高频运??行优化为低频运行。常用场景:滚动条事件、resize事件、动画等,一般每100-500ms执行一次。2.2节流功能的实现方式有定时器版和时间戳版两种。两者各有优缺点。让我们在下面简要地实现它们。2.2.1Timer版本timer版本的throttle功能侧重于使用闭包来保存timer变量,有两个特点:第一次执行会在n秒后执行(timer到时才会触发);stoptriggering节流后的函数会再执行一次(因为函数是延迟的,触发stop时其任务已经进入队列,所以stop后会执行一次)。//定时器版本functionthrottle(fn,wait){lettimer=null;returnfunction(...args){if(!timer){timer=setTimeout(()=>{fn.apply(this,args);timer=null;},wait)}}}2.2.2Timestamp版本timestamp版本的节流功能重点是用闭包保存previous的最后一次,它有两个特点:启动后会立即执行trigger(因为之前的start会被赋值为0);触发器停止后不会执行(因为这个函数是同步任务,触发时会做相应的判断,所以不存在停止触发器再执行的情况)。//时间戳版本functionthrottle(fn,wait){//上次执行时间letprevious=0;returnfunction(...args){//当前时间letnow=+newDate();if(now-previous>wait){previous=now;fn.apply(this,args);}}}2.3效果预览观察效果图可以验证以上理论知识:节流确实降低了内容的输出频率,将高频变为低频;时间戳版本节流功能会输出第一次的内容,但不会输出最后的内容(慎用);定时器版本的节流功能不会立即打印内容,而是在一定时间后打印;另外,其最后输入的内容会被打印出来。
