今天在学习vue的过程中,老师提到了节流的概念,跟着一顿操作以后虽然达到了效果却不是很明白为什么。在万能的b站上找到了小夏老师的视频,进行了学习,并写下这篇博客作为记录。大家感兴趣的可以去b站上看看原视频函数的防抖和节流 – JS 原生面试题,个人认为思路很清晰,讲的非常好!
防抖和节流
防抖和节流是通过某种方式限制函数执行的次数,减少浏览器在执行非常频繁的操作,如:监听滚动条、改变浏览器框大小、ajax请求等,减少不必要的资源浪费。
1. 防抖
1.1 基本方法
通过setTimeout的方式,在一定时间间隔内,多次触发变成一次触发
var btn = document.getElementById('input')
btn.addEventListener('click', submit, false)
function submit(e) {
console.log(1)
}
举个例子,我们希望在不断点击的过程中不会一直触发submit函数,我们可以采用防抖的方式
var btn = document.getElementById('input')
btn.addEventListener('click', debounce(submit), false)
function submit(e) {
console.log(1)
}
//防抖函数
function debounce(fn) {
//设置一个时间
var t = null
return function(){
//如果t不为空,则让t为空,在时间间隔未到时的函数将不会执行
if(t){
cleartTimeout(t)
}
//重新触发一个函数,如果再时间间隔未到之间不会触发
t = setTimeout(function(){
fun()
},1000)
}
}
1.2 防抖中e和this的正确获取
通过这种方式存在着两个问题,一是原本的事件对象该如何获取,二是this指向的问题
我们知道如果在submit函数中打印this和e的话,分别会显示目标元素和触发的事件。那么我们在防抖函数中如何获取正确的e和this呢?e的获取我们可以直接将return的function参数列表添加一个e即可调用
function debounce(fn) {
var t = null
return function(e){
console.log(e) //获取到了事件
if(t){
cleartTimeout(t)
}
t = setTimeout(function(){
fun(e)
},1000)
}
}
但是我们一般不会这样写,因为我们不确定参数的个数,因为e事件存在于argument[0]中,我们可以想办法通过argument获取事件e。
function debounce(fn) {
var t = null
return function(){
console.log(argument[0]) //获取到了事件
if(t){
cleartTimeout(t)
}
//注意此处改成箭头函数,因为直接使用函数argument会指向自身的实参列表
//而不是return的函数的列表
//apply绑定了this指向了return的函数,并将return的函数的参数列表传给了fun
t = setTimeout(() => {
fun.apply(this, argument)
},1000)
}
}
这样我们就获取了一个“没有毛病”的函数防抖!
1.3 进一步的优化
但是考虑到有时候我们需要快速的执行第一次函数,并且达到防抖的效果,也就是执行一次函数后的一段时间内函数都不会执行的效果要怎么办呢?
function debounce(fn, timer) {
var t = null
return function(){
//如果t为null,则firstClick为true,则会在后面执行fn
//如果t不为空,则firstClick为false,则后面不会执行fn
var firstClick = !t
if(t){
cleartTimeout(t)
}
if(firstClick){
fn.apply(this, argument)
}
t = setTimeout(() => {
t = null
}, timer)
}
}
在上述函数中,我们第一次点击时t为null,所以firstClick为true,则会在后面执行fn。当第二次点击的时间未到设置的时间间隔时,t不为空,则firstClick为false,则后面不会执行fn。后面如果一直在未到达时间间隔的时候点击会一直循环下去,直到第n次点击的时间和第n-1次点击的时间超过设定的时间间隔,t才会重归为null,firstClick才会为true,以达到后面执行fn的效果。
2. 节流
节流通过减少一段时间触发的频率来达到和防抖一样的效果
function (fn, delay) {
var begin = 0
return function () {
var cur = new Date().getTime()
//通过时间差来进行判断
if(cur - begin > delay){
fn.apply(this, argument)
begin = cur
}
}
}