一、前言在开发中我们经常会遇到需要频繁触发某个函数的情况比如监听滚动条的变化当滚动条的位置发生变化时需要执行某个函数监听鼠标的移动当鼠标的位置发生变化时需要执行某个函数监听键盘的按键当键盘的某个按键被按下时需要执行某个函数商城抢购疯狂的点击当你频繁的触发用户界面时会不停的触发事件处理函数换而言之当出现连续点击上拉加载、实时搜索等对 DOM 元素频繁操作请求资源加载等耗性能的操作可能导致界面卡顿浏览器奔溃页面空白等情况。而解决这一问题的正是函数节流与函数防抖。二、函数节流1. 定义节约(减少)触发事件处理函数的频率连续每隔一定的时间触发执行的函数它是优化高频率执行一段js代码的一种手段 。这种高频操作若不进行一定的处理必然会造成多次数据的请求服务器的压力这样代码的性能是非常低效的影响性能。降低这种频繁操作的一个重要的手段就是降低频率通过节流控制也就是让核心功能代码在一定的时间隔多长时间内执行一次。节流就是保证一段时间内只执行一次核心代码。2. 特点不管事件触发有多频繁都会保证在规定的间隔时间内真正的执行一次事件处理函数只会让一个函数在某个时间窗口内执行一次若在时间窗口内再次触发则重新计算时间。3. 应用场景鼠标连续多次点击click事件鼠标移动 mousemove、拖拽、窗口尺寸改动(resize)鼠标滚轮页面上拉(onScroll)上拉刷新懒加载等4. 原理:通过判断是否达到一定的时间来触发函数若没有规定时间则使用计时器进行延迟而下一次事件则会重新设定计时器它是间隔时间执行。5. 实现方案时间戳 定时器/* throttle1函数节流实现方式1:时间戳定时器 * params methodduration 第一个参数为事件触发时的真正要执行的函数 * 第二个参数duration表示为定义的间隔时间 * * 原理:通过判断是否达到一定的时间来触发函数若没有规定时间则使用计时器进行延迟而下一次事件则会重新设定计时器它是间隔时间执行不管事件触发有多频繁都会保证在规定内的事件一定会执行一次真正事件处理函数 * */ function throttle1(method duration) { var timer null; var prevTime new Date(); // 之前的时间 return function() { var that this; currentTime new Date(); // 获取系统当前时间 resTime currentTime - prevTime; // 时间戳 // 打印本次当前的世间和上次世间间隔的时间差 console.log(时间差, resTime); // 当前距离上次执行时间小于设置的时间间隔 if(resTime duration) { // 清除上次的定时器,取消上次调用的队列任务重新设置定时器。这样就可以保证500毫秒秒内函数只会被触发一次达到了函数节流的目的 clearTimeout(timer); timer setTimeout(function(){ prevTime currentTime; method.apply(that); }, duration) }else { // 当前距离上次执行的时间大于等于设置的时间时,直接执行函数 // 记录执行方法的时间 prevTime currentTime; method.apply(that); } } } // 事件触发的方法(函数),函数节流1 function handleJieLiu1(){ console.log(节流方式1); } var handleJieLiu1 throttle1(handleJieLiu1, 500); document.addEventListener(mousewheel, handleJieLiu1);定时器 重置一个开关变量/* * throttle2函数节流实现方式2:重置一个开关变量定时器 * params method,duration形参数与上面的含义一致 * return 返回的是一个事件处理函数 * * 在throttle2执行时定义了runFlag的初始值,通过闭包返回一个匿名函数作为事件处理函数, * * 在返回的函数内部判断runFlag的状态并确定执行真正的函数method还是跳出 每次执行method后会更改runFlag的状态,通过定时器在durtion该规定的间隔时间内重置runFlag锁的状态 * */ function throttle2(method, duration){ // 当前时间间隔内是否有方法执行,设置一个开关标识 var runFlag false; // 返回一个事件处理函数 return function(e) { // 判断当前是否有方法执行,有则什么都不做,若为true,则跳出 if(runFlag){ return false; } // 开始执行 runFlag true; // 添加定时器,在到达时间间隔时重置锁的状态 setTimeout(function(){ method(e); // 执行完毕后,声明当前没有正在执行的方法,方便下一个时间调用 runFlag false; }, duration) } } // 事件触发的方法(函数),函数节流2 function handleJieLiu2(){ console.log(节流方式2); } var handleJieLiu2 throttle2(handleJieLiu2, 500); document.addEventListener(mousewheel, handleJieLiu2);上面两种实现函数节流的方式都可以达到防止用户频繁操作而引起重复请求资源的。当鼠标滚轮不断滚动时事件处理函数的执行顺序不一样。当给一个大范围的时间内比如:1小时内每几分钟执行一次超过一小时不在执行推荐使用第一种函数节流的方式。如果仅仅要求间隔一定时间执行一次推荐使用第二种函数节流的方式。三、函数防抖1. 定义防止抖动重复的触发、频繁操作核心在于延迟事件处理函数的执行一定时间间隔内只执行最后一次操作就是当函数被触发后只有在上一次函数执行完一段时间后才会再次触发函数。例如表单多次提交推荐使用防抖。换句话说也就是当连续触发事件时并没有执行事件处理函数只有在某一阶段连续触发的最后一次才执行它遵循两个条件必须要等待一段时间上一次触发的时间间隔要大于设定值才执行2. 特点某段时间内只执行一次。在生活中你可以想象公交司机等人上车后才出站一样3. 应用场景常应用于输入框事件keydown、keyup、搜索联想查询等只有在用户停止键盘输入时才发送Ajax请求。4. 原理它是维护一个计时器规定在duration(延迟)时间后出过事事件处理函数但是在duration时间内再次触发的话都会清除当前的timer重新计时这样一来只有最后一次操作事件处理函数才被真正的触发。5. 实现方案/* * 函数防抖 * 例如假定时间间隔时500ms,频繁不同的操作5s,且每两次执行时间小于等于间隔500ms * 那么最后只执行了1次也就是每一次执行时都结束上一次的执行 * params method,duration,与上面一致 * * 原理:它是维护一个计时器,规定在duration时间后出发时间处理函数,但是在duration时间内再次出发的化,都会清除当前的timer重新计时,这样一来,只有最后一次操作事件处理函数才被真正的触发 * * 一般用于输入框事件,常用场景就是表单的搜索或者联想查询,如果不使用防抖会连续发送请求,增加服务器的压力,使用防抖后,会在用户输入要查询的关键词后才发送请求百度搜索就是这么实现的 * * */ function debounce(method, duration) { var timer null; return function(){ var that this, args arguments; // 在本次调用之间的一个间隔时间内若有方法在执行,则终止该方法的执行 if(timer) { clearTimeout(timer); } // 开始执行本次调用 timer setTimeout(function(){ method.apply(that,args); }, duration) } } // 事件触发的方法(函数),防抖 function handleFangDou(){ console.log(函数的防抖,new Date()); } var handleFangDou debounce(handleFangDou, 500); var oInput document.querySelector(#input); // 获取input元素 oInput.addEventListener(keyup,handleFangDou);如上输入框效果所示每当输入框输入后键盘弹起时执行事件处理函数而不应该是键入内容时都触发一次事件处理函数。同理搜索引擎、表单联想查询功能时不是根据用户键入的字母、数字、内容等同时进行 Ajax 数据请求的。如果每键入一个字母都触发一次数据请求那是会非常耗性能。应当是用户停止输入的时候才去触发查询请求这个时候就用到函数防抖了的。表单的多次提交百度搜索等都是用的防抖实现的。四、对比1. 共同点都是解决频繁操作触发事件处理函数引起页面卡顿不流畅等性能问题都是通过设置延时计时器逻辑来提升性能以减少http请求次数节约请求资源。2. 不同点函数节流间隔时间内执行事件处理函数。函数防抖一定时间间隔内只执行最后一次操作。五、第三方库实现如果不想自己手动实现可以直接引入 lodash 库。安装yarn add lodash或npm i lodash然后引入。1. 节流实现import throttle from lodash/throttle; // 引入lodash.throttle库 // 事件触发的方法(函数),节流 function handleThrottle(){ console.log(函数的节流,new Date()); } throttle(handleThrottle, 500); // 将触发事件处理函数作为第一个参数传入,第二个参数为间隔的时间,这里是500毫秒2. 防抖实现import debounce from lodash/debounce; // 函数防抖 function handleDebounce() { console.log(函数的防抖, new Date()); } debounce(handleDebounce, 500);对于函数的节流与防抖是前端提升性能的手段虽然就几行代码但是面试时常问不衰。让你手写时很多时候拍拍胸脯不借助搜索还真不一定能写得出来。在实际的开发中函数的节流与函数防抖也是比较频繁的可见它的重要性不言而喻。 福利时间如果你正在备战面试或者想要学习其他知识给大家推荐一个宝藏知识库作者整理了一些列 Java 程序员需要掌握的核心知识有需要的自取不谢。知识库地址https://farerboy.com/