防抖节流函数介绍
浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制。
防抖和节流的概念其实最早并不是出现在软件工程中,防抖是出现在电子元件中,节流出现在流体流动中
- 而JavaScript是事件驱动的,大量的操作会触发事件,加入到事件队列中处理。
- 而对于某些频繁的事件处理会造成性能的损耗,我们就可以通过防抖和节流来限制事件频繁的发生; 防抖和节流函数目前已经是前端实际开发中两个非常重要的函数,也是面试经常被问到的面试题。
throw 方法
<script>
function count(x, y) {
if (!x || !y) {
throw new Error('参数不能为空')
//扔掉 throw 配合error使用
}
return x + y //返回结果1+2 =3
}
console.log(count(1, 2))
console.log(count())
</script>
接下来我们会一起来学习防抖和节流函数:
- 我们不仅仅要区分清楚防抖和节流两者的区别,也要明白在实际工作中哪些场景会用到;
- 并且我会带着大家一点点来编写一个自己的防抖和节流的函数,不仅理解原理,也学会自己来编写;
认识防抖函数
我们用一副图来理解一下它的过程:
- 当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间;
- 当事件密集触发时,函数的触发会被频繁的推迟;
- 只有等待了一段时间也没有事件触发,才会真正的执行响应函数;
防抖的应用场景很多:
- 输入框中频繁的输入内容,搜索或者提交信息;
- 频繁的点击按钮,触发某个事件;
- 监听浏览器滚动事件,完成某些特定操作;
- 用户缩放浏览器的resize事件;
防抖函数应用初步了解
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 500px;
height: 500px;
background: #ccc;
font-size: 100px;
text-align: center;
}
</style>
</head>
<body>
<div id="box"></div>
<script src="./lodash.js"></script>
<script>
const obox = document.querySelector('#box')
let i = 1
function mousemove() {
obox.innerHTML = i++
}
// obox.addEventListener('mousemove', mousemove)
// 利用lodash库 实现
// obox.addEventListener('mousemove', _.debounce(mousemove, 500))
// 1. 声明定时器变量
// 2. 每次鼠标移动的时候要先判断是否有定时器 如果有先清除以前的定时器
// 3. 如果没有定时器 执行定时器 存起来变量
// 4. 定时器里写函数
function debounce(fn,t) {
//定时器声明
let timer
// 为什么是return
//返回函数
return function () {
if (timer) clearTimeout(timer)
// 先判断是否有定时器 如果有先清除以前的定时器
timer= setTimeout(function () {
fn() //==mosusemove 鼠标移动
},t) //t==时间
}
}
obox.addEventListener('mousemove',debounce(mousemove,500)) //函数返回函数
//函数合理化
</script>
</body>
</html>
以上代码鼠标移入实现的结果
在例如王者荣耀游戏中:
- 王者荣耀残血回城中移动或者被控制回城会被打断
- 技能cd中不能再释放技能 类似于上面这样的操作就是节流
防抖的简单实现
// 普通方案
window.addEventListener('resize', () => {
console.log('trigger');
})
给窗口绑定了一个resize的事件监听器,如果触发resize就会执行后边的函数,因为resize这个动作会频繁触发,所以为了提高页面的性能,通过一个防抖函数,保证只是在用户调整好位置之后执行后边的函数。
//防抖
function debounce(fn, delay) {
let timer = null;
return function () {
let context = this,
args = arguments
clearTimeout(timer) //关键:清除计时器,从头开始
timer = setTimeout(() => {
fn.apply(context, args)
}, delay)
}
}
function trigger() {
console.log('trigger')
}
window.addEventListener('resize', debounce(trigger, 2000))
//详细了解复制以下代码可以访问原作者
https://blog.csdn.net/weixin_38434088/article/details/115288453
防抖(debounce)
作用是在短时间内多次触发同一个函数,只执行最后一次,或者只在开始时执行。
以用户拖拽改变窗口大小,触发 resize 事件为例,在这过程中窗口的大小一直在改变,所以如果我们在 resize 事件中绑定函数,这个函数将会一直触发,而这种情况大多数情况下是无意义的,还会造成资源的大量浪费。
这时候可以使用函数防抖来优化相关操作:
作者:shawnchenxmu
链接:juejin.cn/post/684490…
function debounce(func, delay, immediate){
var timer = null;
return function(){
var context = this;
var args = arguments;
if(timer) clearTimeout(timer);
if(immediate){
var doNow = !timer;
timer = setTimeout(function(){
timer = null;
},delay);
if(doNow){
func.apply(context,args);
}
}else{
timer = setTimeout(function(){
func.apply(context,args);
},delay);
}
}
}
防抖按钮点击小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn1">防抖请求+1</button>
<button class="btn2" style="margin-left: 10px;">节流请求+1</button>
<script>
const btn1 = document.querySelector('.btn1')
const btn2 = document.querySelector('.btn2')
let a = 0,b = 0
</script>
</body>
</html>
// 防抖方法
function debounce(func,delay){
let timer = null
return function(...args){
// 事件再次触发就清除定时器,打断函数执行
clearTimeout(timer)
timer = setTimeout(()=>{
func.apply(this,args)
},delay)
}
}
// 节流方法一
function throttle(func,delay){
// 设置节流阀
let flag = true
return function(...args){
if(flag){
// 关闭节流阀
flag = false
// 执行函数
func.apply(this,args)
setTimeout(()=>{
// 定时时间结束就打开节流阀
flag = true
},delay)
}
}
}
// 节流方法二
function throttle(func,delay){
// 设置节流阀
let timer = null
return function(...args){
// 只要定时器在执行就关闭节流阀,退出函数执行
if(timer) return
timer = setTimeout(()=>{
func.apply(this,args)
// 等定时时间结束就再次清空定时器,打开节流阀
timer = null
},delay)
}
}
function addA(){
a++
console.log('防抖请求计数值为',a);
}
function addB(){
b++
console.log('节流请求计数值为',b);
}
实现效果
(1)防抖按键:无论鼠标点的多快,只有在点击停下间隔一秒后,才能正常输出递增的数值,否则都不会执行递增和输出操作。
(2)节流按键:无论鼠标点的多快,都会以固定的频率输出递增的数值,输出间隔时间就是给节流函数设置的延时时间,这里设置的是一秒。
节流(throttle)
类似于防抖,节流是在一段时间内只允许函数执行一次。
应用场景如:输入框的联想,可以限定用户在输入时,只在每两秒钟响应一次联想。
可通过时间戳和定时器来实现。
时间戳实现:
var throttle = function(func, delay){
var prev = Date.now();
return function(){
var context = this;
var args = arguments;
var now = Date.now();
if(now-prev>=delay){
func.apply(context,args);
prev = Date.now();
}
}
}
区别在于,使用时间戳实现的节流函数会在第一次触发事件时立即执行,以后每过 delay 秒之后才执行一次,并且最后一次触发事件不会被执行;而定时器实现的节流函数在第一次触发时不会执行,而是在 delay 秒之后才执行,当最后一次停止触发后,还会再执行一次函数。
文中的const是什么
基本用法
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
上面代码表明改变常量的值会报错。
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const foo;
// SyntaxError: Missing initializer in const declaration
上面代码表示,对于const来说,只声明不赋值,就会报错。
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用
if (true) {
console.log(MAX); // ReferenceError
const MAX = 5;
}
本质
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const foo = {};
// 为 foo 添加一个属性,可以成功 foo.prop = 123; foo.prop // 123
// 将 foo 指向另一个对象,就会报错 foo = {}; // TypeError: "foo" is read-only
知识总结:
节流防抖主要了解干什么
没有它会怎样