js中的闭包详解
时隔几个月,今天再看这个闭包有了不同的理解,当时自己可能技术水平还真没现在高,一眼看过去居然没明白问题所在,横线的下面是几个月前对闭包的理解,这句话下面是现在的理解:
今天再回来看闭包是我在看防抖,然后发现了原生js实现防抖是不需要返回值的.这引发了我的思考,然后顺道发现了以前的想法是错误的,下面请看今天的启蒙代码:
let btn = document.querySelectorAll("button");
btn[0].addEventListener("click", like(shuchu, 2000));
function like(a, b) {
let kiss = null;
return () => {
console.log(kiss, "在这里看kiss");
if (kiss) clearTimeout(kiss);
kiss = setTimeout(a, b);
};
}
function shuchu() {
console.log("输出了");
}
比如:我点击了4次这个按钮,正常来说,like()这个函数要执行四次,每一次都会形成一个闭包,每一次闭包里面的变量都是局部变量,也不会被销毁,为什么控制台最后就输出了一次?
这就让我觉得很不可思议,因为我很久都没看闭包了,然后以前理解的闭包可能还不太对.然后我就想啊想,想了好久,想到中间的时候我怀疑他每次执行的都是上一次执行的返回值,
几个月以前我也是这么想的.我甚至还想过Object.definPropoty()来代理到一个对象上面,这样每一次取值就能够知道了,这样就能证明下一次引用了上一次的值,
然后越想越不对劲.咋这么奇怪呢?因为return只有一个,这又不是递归. 我就去看别人将的闭包,再去MDN看了看,最终我再结合上一次对闭包的理解总结出来,如下:
这个闭包其实真的可以理解为一个对象, 怎么说呢:return的这个函数引用他的父函数中的变量,父函数一旦被调用那么就会返回这个子函数,这个子函数又引用父函数,就像这个let a={aa:'11'},
然后b=a一样,b将b里面的aa一改那么a中的aa也会跟着改变.
这里的b就相当于return回来这个函数,a就像是父函数,这个父元素每执行一次就会生成一个b这样的对象,不管父函数执行多少次,他每一次都返回一个全新b这样的对象,而且每次返回的对象都在引用a的地址,
然后呢,每次b都在执行一个操作,这个操作在更改自己对象里面的aa,因为又是引用类型,a里面也会跟这边.这样就实现了多次执行闭包,回回效果不一样的结果
这样就能完全将防抖解释通了:
每一次点击,都会将父里面的kiss重新赋值,然后又给解除绑定,到最后执行最后一次赋值的setTimeout()里面的内容
**这样是不是就更好理解了呢?
为啥有的人说闭包就像一个对象一样,执行一次然后赋值给一个变量,那么这个变量其实就相当于上面所说的b了,然后多次的执行这个变量,就相当于多次的在操作b,不管操作多少次他们操作的值永远都是父函数里的数据
**
闭包:闭包的应用直接上代码理解:
function doSth(t,cb){
return function(){
if(--t === 0){
cb()
}
}
}
function logSth(){
console.log('嘿嘿嘿');
}
let fn=doSth(4,logSth); //这里返回里面嵌套的按个函数
fn() //这里执行返回的函数,当前t=3
fn(); //这里执行返回的函数,当前t=2
fn(); //这里执行返回的函数,当前t=1
fn(); //输出 '嘿嘿嘿'
每一次的结果其实都不一样;
总结一下:
闭包其实就相当于,一个外层函数,一个内层函数,内层函数依赖外层函数中的属性或者方法.外层函数将这个内层函数作为返回值return;也可以直接不声明变量,直接去接受一个匿名函数.这样都可以形成闭包:下面给出解释
闭包类型①
//在全局声明一个变量
function doSth(){
num=999
index='好小子',
like='你居然学会了闭包?'
//返回一个匿名函数
return function(){
console.log(index,num++,like)
}
}
这样就形成了闭包的一种类型
闭包类型②
function doSth(){
num=999
index='好小子',
like='你居然学会了闭包?'
bibao=function (){
console.log(num,index,like)
}
}
//这里需要注意,第②种类型的闭包里面的bibao前面不能够加声明关键词,比如var let const,加了这个函数就只是doSth里面的一个函数了
上面两种类型的闭包
第一种类型的闭包需要先执行一次最外层的函数,然后将返回值赋值给一个变量,这个变量可以是全局里面的变量,也可以是别的函数内部的变量.如果作为在别的函数内部变量,就需要你这个最外层函数是一个全局函数,这个最外层函数不会和别的函数形成闭包.
返回上面这一样说的两种变量:
①全局变量:如果是赋值给了一个全局变量,那么其实就相当于你在全局声明了一个对象了.所有人都可以通过调用这个变量的执行结果去获取到里面的属性值,就好像一个全局对象你去.出来里面的属性值一样
②局部变量:如果是赋值给了一个局部变量,那么起始就相当于你在这个局部变量里面声明了一个对象.这个局部变量所在的作用域里面的所有人都可以来访问到这个里面的属性值了.就相当于你在局部定义了一个对象,然后.出来这个对象里面的属性值一样
第二种类型的闭包:不需要执行外层的那么函数,因为赋值的变量直接就是属于window的,也就是相当于上面说的直接在全局里面声明了一个 对象,想要是用里面的属性值直接点就行,这里就是直接执行这个变量就行
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)