闭包
定义
一个函数和其对周围状态的引用捆绑在一起,这个组合就叫做闭包。简单理解,函数A返回一个函数B,并且函数B引用了函数A的私有变量,函数A外部有变量接收函数B,这时就产生了闭包。
与普通函数的区别
闭包函数跟普通函数的区别在于,普通函数执行完之后会销毁执行空间,而闭包的产生就是为了阻止垃圾回收将闭包函数执行空间回收。
优缺点
优点:延长变量的生命周期,因为执行空间没有被销毁,所以变量一直存在内存中;可以访问函数内部的私有变量,根据作用域链查找原则,返回的函数会往上层函数进行变量的查找
缺点:因为执行空间不会被销毁,所以会一直占用内存,如果占用的内存过多,就会导致内存泄露,所以闭包需慎用,用完闭包需要及时把承接闭包的变量赋值为null
// 利用闭包的方式得到当前小li的索引号
for(var i = 0; i < lis.length; i++){
(function(i){
// 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的变量i
lis[i].onclick = function(){
console.log(i);
}
})(i);
}
// 利用闭包应用 3秒后打印li里面的内容
for(var i = 0; i < lis.length; i++){
(function(i){
// 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的变量i
setTimeout(function(){
console.log(i);
}, 3000);
})(i);
}
函数柯里化
使用闭包实现函数柯里化
定义
函数柯里化:柯里化又称为部分求值,函数接收了参数之后不会立即求值,而是形成一个闭包将参数保存起来,然后继续返回一个函数,等需要求值的时候才会将之前保存的参数进行求值返回
利用柯里化实现currying(1,2)(3)(4,5) => 15
function currying(...argCurrying){
const sum = function(...argSum){
argCurrying = [...argCurrying, ...argSum]
return sum
}
sum.toString = function(){
return argCurrying.reduce((pre, next) => pre + next)
}
return sum
}
console.log(currying(1,2)(3)(4,5).toString()) // 15
节流防抖
利用闭包实现节流防抖
定义
节流和防抖是一种优化高频触发事件的手段
节流:规定在n秒内只执行一个事件,不管在n秒内你触发的多少次,最后只执行一次
防抖:在用户停止事件n秒后才触发,在n秒内重复触发,都会重新计时
// 节流
inputElement.oninput = (function(flag){
return () => {
if(!flag) return
flag = false
setTimeout(() => {
console.log(inputElement.value)
flag = true
}, 1000)
}
})(true)
// 防抖
inputElement.oninput = (function(timer){
return () => {
clearTimeout(timer)
timer = setTimeout(() => {
console.log(inputElement.value)
}, 1000)
}
})()
区别
相同点:
- 都可以利用 setTimeout 实现
- 目的都是控制事件触发的频率,降低服务器压力
不同点:
- 函数节流只在n秒内执行一次,不管在此期间点击了多少次,都只执行一次;函数防抖只在事件停止触发n秒后才执行,在n秒内又触发了事件,则又开始重新计时
- 函数节流通过一个节流阀控制 setTimeout 的是否执行,节流阀处于关闭状态时被触发了,则直接 return;函数防抖通过不断地 clearTimeout 跟 setTimeout 实现
应用
节流在间隔一段时间执行一次回调的场景:
- 滚动加载,加载更多或滚动到底部监听
- 搜索框,搜索联想功能
防抖在连续的事件,只需触发一次回调的场景有:
- 搜索框搜索输入。只需用户最后一次输入完,再发送请求
- 手机号、邮箱验证输入检测
- 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染