宝哥软件园

关于JavaScript闭包

编辑:宝哥软件园 来源:互联网 时间:2021-08-26

最近朋友在采访中被问到JS关闭的事情。我一时语塞,想起了原华的一句话:“这道题太难了,我做不到,我做不到!”。

JS闭包是面向对象的一个重要知识点,于是我开始了一段渐行渐远的旅程。

闭包意味着外部函数的AO对象被内部函数引用,不能被释放。

上面这句话听起来可能不是很能理解。之前在Python闭包笔记里写过一篇关于Python闭包的文章,写的是百度百科对闭包的理解。虽然大部分都是引用别人的知识结构,但是语言是相通的,不需要记住那些晦涩难懂的名词。对于闭包,作为初学者,我们只需要知道:

函数作为返回值,函数作为参数。可以理解为一种封闭。

话不多说,先用最后一段代码缓解尴尬的气氛:

函数outer(){ var max=10;函数inner(num){ if(num max){ console . log(num)} }返回inner;} var foo=outer();foo(20);//20以上代码满足函数是返回值的条件,所以是闭包函数。

根据JS函数的执行机制,会先执行第10行的foo代码,执行完函数后会通过JS垃圾回收机制回收外部函数,但是执行到第3行时,我们发现外部函数内部还有另外一个内部函数,内部函数引用了外部函数的max=10执行到第11行时,不能回收并留在内存中的变量,因为在外部函数中max=10是留在内存中的,所以它会被内部函数调用并满足if条件判断,所以输出20;

上面我们实现了一个简单的闭包函数,但是有一个问题,就是不能释放的对象留在内存中,造成了不必要的内存开销。

请看下面的代码:

var max=10,foo=function(num){ if(num max){ console . log(num);} };(函数(bar){ var max=100;bar(20)})(foo);//20以上代码满足函数作为参数传递的条件,所以是封闭函数。

函数foo作为参数传递到函数中,并分配了一个bar参数。当执行到bar()函数时,函数里面的max不是100,而是10,看起来不可思议。让我们暂时将第7-10行中的函数称为“父范围”,其余的称为“全局范围”。当执行bar(20)时,函数将执行第2行的代码,此时foo函数内部的max取值,max=10正好在他的“全局范围”内,所以max=10才会被取;的值而不是max=100的价值。可以看出,取值时,需要在这个函数的范围内创建值,而不是所谓的“父范围”或靠近函数的地方。

让我们看看另一段代码:

var num=20函数outer(){ var max=10;function inner(){ if(num max){ console . log(num);} }返回内部;}var foo=outer(),num=30foo();//30看完上面的解释,上面的代码可以知道它是一个闭包函数,并且定义了一个全局变量num,最初定义为num=20。当代码执行到第11行时,它调用执行第2行,当执行第11行时,执行第12行。这时,全局num=20已更改为num=30然后执行第13行。当在执行过程中调用内部函数时,我们可以从输出中看到,被调用的num是稍后分配的30。

可以看出全局num变量被污染了。

让我们看看下一个代码:

函数outer(){ var max=10;函数inner(num){ if(num max){ console . log(num);} }返回内部;}var foo=outer(),max=100foo(20);//20在上面的代码中,执行函数时,首先执行第10行的函数,然后调用第1行的函数。此时,max被赋值为10,但需要注意的是,max=10此时;不是在全局范围内,而是在outer()函数的范围内,执行第十行,然后执行第十一行。此时,max被指定为100,但需要注意的是,max=100此时;在全球范围内。因此,执行第12行代码时,调用了inner()函数,传入了参数20,输出结果为20,说明outer()函数范围内的object num没有被全局object num污染。

从以上四个代码中,我们对闭包的一些基本特性有了初步的了解。但是,由于缺乏知识,我们担心总结不够全面。这时我们突然想到了东东大神的笔记,于是在网上搜了一些。我们再总结一下。

闭包:一种不仅重用变量,而且保护变量免受污染的机制。

为什么使用闭包:

全局变量和局部变量各有利弊。

全局变量:

优秀的:可以重复使用,缺少:容易被污染。局部变量:

You :只能在函数中使用,不会被污染。你:不能重用!何时使用:

只要你重用一个变量并保护它不受污染。

如何使用:

1.用外部函数包装要保护的变量和内部函数。2.外部函数将内部函数返回到外部。3.调用外部函数,获取内部函数的对象,保存在外部变量中形成闭包。闭合形成的原因:

调用外函数后,外函数的AO对象不能被释放,被内函数引用。

闭包的缺点:

它比普通函数占用更多的内存。解决方法:封口不用时,要及时松开。引用内部函数对象的变量被赋为null。结合栗子和东东的笔记,我们对闭包已经有了一个形象的认识,但是要达到全面的认识,只能说革命没有成功,同志们还需要努力。

可喜的是,东东对闭包的图形化解释已经在网上找到了。看完之后,相信大家对闭包会有更深的理解。

让我们从一段代码开始,以缓解许多单词的尴尬:

//1.包装要保护的变量和内部函数函数outer(){ var I=1;//2.外部函数将内部函数对象返回给外部返回函数(){ console . log(I);}}//3.调用外部函数获取内部函数对象var getNum=outer();//GetNum : FuncTion(){ console . log(I);} GetNum();//1 GetNum();//2i=1;getNum();//3 GetNum();//4上面的代码定义了一个outer()函数,I=1;在外部函数的范围内定义;内部返回一个函数,形成一个闭包。当代码执行到第10行时,它实际上返回了outer()函数的内部函数。当执行一次getNum()时,输出结果为1,因为打印了I(注意:如果打印了I,输出结果为2)。再次执行getNum(),因为我之前执行过I一次,所以执行结果是2。全局设置i=1,执行getNum()两次,执行结果分别为3和4,说明i=1全局没有覆盖outer()函数范围内的I值,outer()函数中的I值得到了很好的保护和重用。

让我们来看看东东对上述代码的图形化分析:

如上所示,在JavaScript中有一个执行环境堆栈(ECS)的概念。注意:ECS=本地EC,全局EC,所有函数都要通过推栈和弹出栈来执行。在执行环境堆栈中有一个独立的全局EC of main()函数,它指向全局窗口范围,该范围指向全局窗口对象。当代码运行到红线时,执行环境堆栈中只有一个全局执行环境窗口。此时,窗口中有两个全局变量(标识符):outer和getNum,其中outer()函数打开一个内存,用于存储执行的方法,并通过作用域记住其父方法。

如上图:执行outer()函数时,outer()相当于本地EC,进入执行环境栈。此时,outer()将打开自己的作用域(AO),其中定义了i=1的环境变量。因为I对象在window中被引用,所以outer的AO会指向window,getNum会调用outer()函数并返回一个方法,所以会打开一个内存来存储执行的方法。在这种方法中,I变量指向外部的AO,绿线相互牵连。

如上图:当执行环境栈中的outer()函数完成栈的执行时,理论上,outer的AO,也就是蓝盒,应该被垃圾回收机制回收,但是由于闭包的原因,这一块被留下了,闭包就在这里形成了。

如上图所示:当outer()函数从堆栈中出来时,getNum()函数进入堆栈,getNum打开自己的作用域(AO)并执行I一次。此时,输出结果为1。

如上图所示:当getNum()函数从栈中出来时,自身打开的作用域被回收,但由于闭包的原因,outer的作用域保留在内存中,变成i=2。

如上图所示,再次执行getNum()函数相当于再次将getNum()函数放入和放出堆栈。i=2,最初由于关闭而保留,再次运行。

如上图所示:接下来执行i=1,即I对象被添加到全局窗口。这时,我在外层的范围由于最后一个而变成了3。

如上图所示:第三次执行getNum()函数。这时,每个人都应该知道如何执行它。getNum()不去全局窗口取i=1使用,而是去创建的作用域取值,即i=3操作。

至此,已经介绍了闭包的所有运行过程。你对闭包有清晰的理解吗?

不用担心,还是有点短,就是主动释放闭包产生的内存。如下

//1.包装要保护的变量和内部函数函数outer(){ var I=1;//2.外部函数将内部函数对象返回给外部返回函数(){ console . log(I);i=null}}//3.调用外部函数获取内部函数对象var getNum=outer();//GetNum : FuncTion(){ console . log(I);} GetNum();//1 GetNum();//0i=1;getNum();//0GetNum();//0我们在第一次执行getNum()函数后将I变量设置为null,发现再次执行getNum()函数时结果变成了0,说明outer()函数中的I变量内存已经释放!

至此,已经解释了JavaScript闭包的所有内容。以上内容如有瑕疵,请批评指正。

好记性不如坏记性。特此记录,与大家分享!

以上是边肖介绍的JavaScript闭包的详细解释和集成,希望对大家有所帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!

更多资讯
游戏推荐
更多+