网上主流解释
:函数防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。笔者解释
:一句话总结
:用户连续多次触发某个事件,则只执行最后一次网页性能优化
技术,因此初学者刚开始学习会有一些吃力,并且很多网站都没有做防抖处理(性能优化)没有做函数防抖处理的用户体验如下
第一张
,此时用户想看第五张
。正常情况下,鼠标会依次触发 第二、第三、第四张的移入事件,但这不是用户真正想要触发的(误触发)有做函数防抖处理的用户体验如下
第一张
滑动到第五张
,由于鼠标在 第二、第三、第四张停留时间很短(假设小于0.5秒),所以判定为用户误触发,则不触发对应的事件处理函数注意点
:定时器中的this默认为window,需要使用上下文模式bind()
动态修改为当前事件源<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>动画-案例《手风琴》</title> <style> * { margin: 0; padding: 0; } ul { list-style: none; width: 2400px; } #box { width: 1200px; height: 400px; border: 2px solid red; margin: 100px auto; overflow: hidden; } #box li { width: 240px; height: 400px; float: left; } </style> </head> <body> <div id="box"> <ul> <li><img src="./images/collapse/1.jpg" alt=""></li> <li><img src="./images/collapse/2.jpg" alt=""></li> <li><img src="./images/collapse/3.jpg" alt=""></li> <li><img src="./images/collapse/4.jpg" alt=""></li> <li><img src="./images/collapse/5.jpg" alt=""></li> </ul> </div> <script src="./animation.js"></script> <script> /* 1. 鼠标抖动 : 例如用户鼠标轻微晃动,快速划过某一个元素(用户本身不想触发,只是鼠标误触发,常见于鼠标事件 移入/移出/移动 ) 2. 函数防抖 : 用户连续多次触发某个事件,则只执行最后一次 * 如果用户鼠标轻微晃动,在某一个元素上停留时间很短,则认为是用户误触发,则不执行本次事件处理函数 * 影响 : 会触发一些本没有必要触发的事件,浪费代码性能 3. 函数防抖解决思路 : 3.1 声明全局变量存储定时器ID 3.2 每一次移入元素时 : 先清除上一次的定时器 * 以最后一次触发为准 3.3 开启定时器,间隔时间才会触发事件处理函数 * 用户连续触发事件的时候,全局变量只会存储最后一次触发的事件定时器(前面的都被清除了,认为这是用户误操作产生抖动) */ /*需求 (1):给每一个li设置鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100 (2):鼠标移出大盒子,所有的li的宽度都变成默认的240 */ //1.获取元素 var liList = document.querySelectorAll('#box li');//li元素列表 var box = document.querySelector('#box');//li元素列表 //2.注册事件 // 函数防抖一 : 声明全局变量存储定时器 var timeID = null; //2.1 鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100 for (var i = 0; i < liList.length; i++) { liList[i].onmouseover = function () { //this:事件源 // 函数防抖二 : 每一次移动新的li元素,移除旧的定时器,以最后一次触发为准 clearTimeout(timeID); //函数防抖三 : 间隔500ms后执行 /* 注意点:定时器中的this默认是window,需要使用bind修改为事件源 */ timeID = setTimeout(function () { //3.事件处理函数 (触发事件) console.log(1111); //3.排他思想修改样式 for (var j = 0; j < liList.length; j++) { if (liList[j] == this) {//是自己 animationSlow(liList[j], { width: 800 }); } else { animationSlow(liList[j], { width: 100 }); } }; }.bind(this), 500); }; }; //2.2 鼠标移出大盒子,所有的li的宽度都变成默认的240 /* 这个事件目前对我们的函数防抖会有影响 : 事件冒泡 */ /* jq第一天就会讲onmouseout对事件的影响 */ // box.onmouseout = function(){ // for(var i = 0;i<liList.length;i++){ // animationSlow( liList[i] , {width:240}); // }; // }; </script> </body> </html>
<script> //1.获取元素 var liList = document.querySelectorAll('#box li');//li元素列表 var box = document.querySelector('#box');//li元素列表 //2.1 鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100 for (var i = 0; i < liList.length; i++) { liList[i].onmouseover = function () { //this:事件源 //调用防抖函数, 防抖间隔为500ms debonce(function () { //3.事件处理函数 (触发事件) console.log(1111); //3.排他思想修改样式 for (var j = 0; j < liList.length; j++) { if (liList[j] == this) {//是自己 animationSlow(liList[j], { width: 800 }); } else { animationSlow(liList[j], { width: 100 }); } }; }.bind(this), 500); }; }; /** * @description: 万能防抖函数 * @param {type} fn : 事件处理函数 * @param {type} timeout : 防抖时间间隔 * @return: */ function debonce(fn, timeout) { /* 核心技术点 1.函数防抖需要使用定时器id, 这个id不能是局部的(函数走完之后会被回收) 2.定时器id也不能是全局的,造成全局变量污染 3.解决方案: (1)使用闭包延长局部变量生命周期,但是闭包语法很繁琐 (2)利用函数本身也是对象,使用函数本身的静态成员来存储定时器ID */ //1.先清除上一次触发事件 的定时器 clearTimeout(debonce.timeID); //2.以最后一次触发 为准 debonce.timeID = setTimeout(fn, timeout); }; </script>
高频事件
高频事件
: 触发频率极高的事件。例如 滚动条事件onscroll
鼠标移动onmousemove
网页大小变化onresize等
高频触发
:事件本身不是高频的,但是用户可以通过很快的手速来触发。例如用户疯狂快速点击 抢购按钮(onclick,onmousedown)浪费资源,降低网页速度
,甚至导致浏览器卡死:由于高频事件触发非常频繁,可能1秒钟会执行几十次甚至上百次,如果在这些函数内部,又调用了其他函数,尤其是操作了DOM (DOM操作耗性能且可能导致浏览器出现回流) ,不仅仅会降低整个网页的运行速度,甚至会造成浏览器卡死,崩溃。网络堵塞
: 如果在高频事件中,进行了重复的ajax请求,可能会导致请求数据出现混乱,并且还占用服务器带宽增加服务器压力 (间接增加服务器成本)==限制事件处理函数的执行周期==
节流间隔
//函数节流1 : 声明变量记录上一次事件触发时间 var lastTime = 0; //1.1 鼠标移动事件 var i = 0; window.onmousemove = function () { //函数节流2 : 判断触发时间间隔 (0.5秒 = 500ms) var currentTime = new Date().getTime(); //当前时间 if (currentTime - lastTime > 500) { //事件处理函数 i++; console.log('鼠标移动事件被触发' + i + '次'); //函数节流3 : 事件成功触发之后,当前时间 成为 下一次触发间隔参考时间 //说人话:现任女友已经结束了, 她已经成为下一任女友 故事里的 前任女友 lastTime = currentTime; }; };、
节流函数的事件处理
都是不一样的,他们可能是鼠标移入、鼠标移出、鼠标移动。 每一个案例需要的节流间隔
也不同/** * @description: 函数节流 * @param {type} fn: 事件处理函数 * @param {type} timeout: 节流间隔 * @return: */ function throttle(fn, timeout) { /*核心技术介绍 1. 函数节流需要使用变量来存储 上一次触发时间 2. 这个变量如果是局部变量 : 则函数完毕会被回收。 如果是全局变量:则会造成全局变量污染 3.解决方案 : 利用函数本身也是对象,使用函数本身的静态成员来存储 上一次触发时间 */ //给throttle添加静态成员lastTime if(!throttle.lastTime){ /* 为什么一定要有这一步呢? 因为函数对象的属性不存在时,默认取值会得到undefined,而undefined在做数学计算 的时候会转成number类型得到NaN. Number(undefined) 结果是NaN。无法计算 */ throttle.lastTime = 0; }; //1.记录当前时间 var currentTime = new Date().getTime(); //2.判断触发时间间隔 if (currentTime - throttle.lastTime > timeout) { fn(); //3.将当前时间作为 下一次触发时间 参考时间 throttle.lastTime = currentTime; }; };
//1.2 滚动条事件 var j = 0; window.onscroll = function () { //节流函数 : 滚动条 0.5s 只会执行一次 throttle(function () { j++; console.log('滚动事件被触发' + j + '次'); }, 500); }; //2. 高频触发 :事件本身触发不频繁, 但是用户可以通过疯狂触发来实现高频 var btn = document.querySelector('#btn'); var p1 = document.querySelector('#p1'); var k = 0; btn.onclick = function () { //节流函数 : 按钮1s 只能点击一次 throttle(function () { k++; p1.innerText = '点击事件被触发' + k + '次'; }, 1000); };
1.函数防抖:用户多次触发事件,以最后一次触发为准
2.函数节流:限制事件的执行周期(500ms内指挥执行一次)
3函数防抖与函数节流的异同点 与应用场景
(1)相同点:都是为了优化js代码执行频率,提高代码的性能
(2)不同点:
函数防抖
:由用户需求决定
a.鼠标移入移出,用户快速移动鼠标,应该等用户结束后,以最后一次为主
b.输入框事件:验证手机号或者邮箱,用户输入时不断触发键盘事件,应该等用户结束输入之后,以最后一次输入为准
函数节流
:由事件本身决定 (高频事件)
a. onscroll
: 高频事件(滚动条事件)
b. onmousemove
:高频事件(鼠标移动)
* 特殊情况:高频触发 (抢购按钮点击,本身不是高频事件,用户可以用手速实现点击高频)
原创声明,本文系作者授权云+社区发表,未经许可,不得转载。
如有侵权,请联系 yunjia_community@tencent.com 删除。
我来说两句