前言
在前端实际开发中,有关JS原生的节流和防抖处理也是很重要的点,关于底层和原理的掌握使用,尤其是在性能优化方面甚为重要。作为前端开发的进阶内容,在实际开发过程中节流和防抖通常都是项目优化的必要手段,而且也是非常常用的优化手段,因为在前端程序中防止用户短时间内快而高频地多次操作触发动作执行,最常用的就是页面提交按钮,为了防止用户多次点击重复提交触发表单多次提交的问题,还有就是为了防止用户在拖动滑动条的时候,多次触发加载更多等情况。所以在JS中节流与防抖的使用也是比较常用的知识点,而且在前端求职面试的时候二者也是必考知识点,可以说非常重要,那么本文就来做一下总结,方便查阅使用。
闭包
由于在JS中的节流与防抖都是属于闭包的应用范畴,所以想要理解防抖和节流,首先要理解闭包。
闭包:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
通俗的说,闭包就是能够读取其他函数内部变量的函数。由于在 JavasSript 语言中,只有函数内部的子函数才能读取局部变量,所以闭包可以简单理解成"定义在一个函数内部的函数"。在本质上看,闭包是将函数内部和函数外部连接起来的桥梁。 想要了解闭包的详细内容可以阅读三掌柜的这篇文章:前端开发:JS中闭包的使用详解前端开发:JS中闭包的使用详解_js闭包的使用_三掌柜666的博客-CSDN博客
JS节流与防抖
在前端开发过程中,为了提高应用程序性能,在用户进行窗口操作和输入框操作时候多次提交或者滑动的时候,如果未对事件处理函数调用频率做限制的时候,会增加浏览器和服务器的负担,造成用户体验感非常差,甚至有些滑动事件由于自身复杂的计算回调函数频繁调用会引起前端页面的卡顿。还有就是在绑定scroll、resize事件时,当它发生的时候被触发的频率非常高、间隔很近,尤其是日常开发的页面中,事件中涉及到大量的位置计算、DOM操作、元素重绘等等,这些都无法在下一个scroll事件出发前完成,就会引起浏览器的卡帧,严重影响用户体验感。
针对这些问题,提高用户体验,优化程序性能,就可以采用节流和防抖的方式来解决这些问题,把多次计算汇合成一次计算,只操作一个精确点即可,这样可以减少事件函数的调用频率,减轻了浏览器负担,提高了程序性能,提高了用户体验。
另外,防抖动和节流本质是不一样的。防抖是多次触发但最后只会执行一次,节流是多次触发但周期内只会执行一次。函数节流与函数防抖都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。接下来就来详细解答二者具体的区别。
节流是什么?
节流(throttle):当持续触发事件时候,保证一定时间段内只调用一次事件处理函数,也就是持续触发事件,每隔一段时间只执行一次函数。节流的策略是,在每个时间周期内,不论触发多少次事件,只执行一次动作。在上一个时间周期结束后,如果又有事件触发,则开始新的时间周期,同理,在新的时间周期里只执行一次动作。 和防抖类似,节流策略也分前缘和延迟两种。延迟是指周期结束后执行动作,前缘是指执行动作后再开始周期。
节流使用场景:有很多,比如图片懒加载的使用、进行ajax数据请求加载的使用、下滑自动加载数据、侧边浮动导航栏等等。
函数节流:指在一定时间周期内执行的操作只执行一次,也就是预先设定一个执行周期,当调用动作的时间大于等于执行的周期则执行该动作,接着进入下一个新周期。一个函数执行一次后,只有大于设定的执行周期后才会执行第二次。
节流函数的实现
节流函数的适用场景主要在拖拽场景和缩放场景中,如固定时间内只执⾏⼀次,防⽌超⾼频次触发位置变动;以及监控浏览器的resize等场景。具体实现示例如下所示:
const throttle = (fun, delay) => { //fun是事件处理程序,delay是事件执行的延迟事件,单位为毫秒
let timer = null;
return (...args) => {
clearTimeout(timer); //每次触发事件,需要把定时器清理之后重新计时
timer = setTimeout(() => {
fun.apply(this, args); //执行事件处理程序
timer = null; //事件执行之后把清除定时器,待下次触发事件的时候重新设置
}, delay);
};
};
防抖是什么?
防抖(debounce):在持续触发事件的时候,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。防抖一定是在事件触发N秒后才会执行,若在一个事件触发的N秒内又触发了该事件,以新的事件时间为准,N秒过后才执行,等触发事件N秒内不再触发事件才执行。也就是多次快速频繁地触发事件,只会执行一次事件函数,前提是需要加上一个时间限制。也可以理解为,为了防止快速且频繁的触发事件而导致多次执行事件函数引起程序卡顿等问题,可以设置在多次触发的事件只执行一次事件函数,且需要设定一个间隔时间,若两次事件触发的间隔时间低于设定的时间,就是频繁操作。
防抖的特点是当事件快速连续不断触发时,动作只会执行一次。分为两种,一种是延迟debounce,一种是前缘debounce。 延迟debounce,是在周期结束时执行,前缘debounce,是在周期开始时执行。但当触发有间断,且间断大于我们设定的间隔时间时,动作就会有多次执行。
防抖使用场景:有很多,比如监听滚动事件、鼠标移动事件onmousemove、频繁点击表单提交按钮等。
函数防抖:是指在一定时间内,在动作被连续频繁触发的情况下,动作只会被执行一次,也就是当调用动作过N秒后,才会执行该动作,若在这N秒内又调用该动作则将重新计算执行时间,所以短时间内的连续动作只会触发一次。
防抖函数的实现
防抖函数的适用场景主要在防止多次提交场景,比如防⽌多次提交按钮,只执⾏最后提交的⼀次;输入框输入的时候,只执⾏⼀段连续的输⼊事件的最后⼀次;以及搜索框进行搜索联想词功能。具体实现示例如下所示:
const debounce = (fun, delay) => { //fun是事件处理程序,delay是事件执行的延迟事件,单位为毫秒
let timer = null;
return (...args) => {
clearTimeout(timer); //每次触发事件,需要把定时器清理之后重新计时
timer = setTimeout(() => {
fun.apply(this, args); //执行事件处理程序
}, delay);
};
};
防抖与节流注意事项
在使用节流和防抖的时候,要注意以下的点:
①闭包的运用
实际中要避免全局的命名污染,所以不建议使用全局变量,同时为了让所需要的变量能够得到缓存,所以使用闭包存储部分需要用到的变量。
②this指向以及event参数
由于在事件触发调用的时候,是由防抖或者节流函数return返回出来的函数,而不是事件函数,事件函数是作为参数传入防抖或者节流函数中的,所以它的this指向是window,而不是触发事件调用函数的dom。同理可知,事件自带的event参数也是拿不到的,所以需要通过闭包去存储this上下文以及argument这个传递给函数的参数的类数组对象。事实上,所有的高阶函数内部都需要特别注意this的绑定 。
节流与防抖的总结
防抖:其实就是维护一个定时器,把多个相同的操作合并成一个操作,规定在delay后触发函数,若在此之前触发函数,则取消之前的计时重新计时,只有最后一次操作才能被触发。
节流:原理就是判断是否达到一定的时间周期来触发事件,规定某个时间段内只能触发一次函数。
节流与防抖区别:防抖只会在最后一次事件后执行触发函数;节流不管操作事件多么的频繁都会保证在规定时间段内触发事件函数。
最后
通过本文关于前端开发JS中的节流与防抖的详细介绍,不管是在实际的前端开发工作中还是在前端求职面试中都是非常关键的知识点,所以作为前端开发者来说必须要掌握它相关的内容,尤其是从事前端开发不久的开发者来说尤为重要,是一篇值得阅读的文章,重要性就不在赘述。欢迎关注,一起交流,共同进步。