防抖与节流
防抖是在事件被频繁触发时,只执行最后一次,而节流是在一定时间内只执行一次。
- 就相当于防抖就像按住一根弹簧直到松手才会弹起来
- 而节流就像在ktv轮流唱歌,等你唱完了下一个人才能唱
防抖函数的核心机制就是闭包,当每一次点击会产生debounce执行上下文,随后debounce执行完其上下文又被反复销毁,但是其中的变量timer又始终保持着对function外部的引用,于是由此形成了闭包。
let btn = document.getElementById('btn')
function handle(e){
console.log(`提交时间:${new Date().toLocaleTimeString()}'); // 换成ajax请求
}
// 创建专门的debounce函数用于防抖,把handle交给debounce处理
btn.addEventListener('click', debounce(handle))
// 防抖函数
function debounce(fn,delay=5000){
let timer = null; //外部函数的变量,接收定时器返回的ID
return function(e) {// 内部函数(闭包载体)
clearTimeout(timer);// 引用外部变量 timer
timer = setTimeout(() => {
//确保 handle 函数中的 this 始终指向触发事件的元素(在这里是 btn)
//并且事件对象 e 作为参数传递给 handle 函数。
//这样,无论 handle 是直接调用还是通过 setTimeout,它的 this 上下文都保持不变。
fn.call(this,e);
}, delay)
}
}
- 其中
debounce返回一个函数体,跟debounce形成了一个闭包。 - 子函数体中每次先销毁上一个
setTimeout,再创建一个新的setTimeout。
什么是闭包?
在JavaScript中,变量的作用域是词法作用域,即函数内部可以访问外部作用域的变量,但外部作用域无法访问内部作用域的变量。
闭包就是内部函数可以访问外部函数的变量,即使外部函数已经执行完毕。
比如下方代码中,bar函数内部访问了myName变量,而myName是在foo函数的作用域中定义的。当foo执行完毕后,通常情况下,其内部的局部变量会被垃圾回收机制回收。然而,由于bar函数被返回并被外部变量fn引用,而bar函数又引用了foo中的myName变量,这就形成了一个闭包,使得myName变量不会被回收,从而在调用fn()时仍然可以访问到“小明”。
function foo(){
function bar(){
var age = 18
//正是因为这里引用了外部函数变量myName。反之如果内部函数没有引用任何外部函数的变量,即使返回了该函数,也不会形成闭包。
console.log(myName);// 引用外部变量
}
var myName = '小明'// 被闭包捕获的变量
return bar
}
//foo执行完毕之后就会被垃圾回收机制回收,而这里的return bar返回bar函数后就会被销毁
var myName = '小红'// 全局变量
var fn = foo()// 执行foo,返回bar(此时闭包已形成)
fn()// 输出"小明"(闭包保存了foo作用域中的myName)