手机版

JavaScript知识点总结(十六)Javascript闭包代码详解

时间:2021-10-28 来源:互联网 编辑:宝哥软件园 浏览:

闭包是Javascript语言的难点,也是它的特点。许多高级应用程序依赖闭包来实现它。我接触闭包的概念很久了,但是一直很困惑,一直不能够理解JavaScript的闭包是什么,是为了什么。今天在网上看到一篇关于JavaScript闭包的文章(原文链接),非常不错。现在我已经彻底理解了JavaScript的闭包是什么以及它的用途。我想把它写在这里,和你分享。希望不懂JavaScript闭包的朋友看完能看懂!以下内容大部分来自原文。我会在原文的基础上增加一些代码注释、运行渲染和一些修改,方便大家理解!

第一,变量的范围。

要理解闭包,我们必须首先理解Javascript的特殊变量范围。

在JavaScript中,变量有两个作用域:全局变量和局部变量。

在Javascript中,可以直接从函数内部读取全局变量。

var n=;//定义全局变量nf function f(){ alert('访问函数内部的全局变量n,n=' n ');//访问函数内部的全局变量n } f();//运行结果:

但反之亦然,函数中的局部变量不能在函数外读取。

函数f(){ var n=;//在f函数内部定义局部变量n}alert('访问函数外部的局部变量n,n=' n ');//访问函数外的局部变量n。错误:n未定义运行结果:

这里有一点需要注意。在函数内部声明变量时,必须始终使用var命令。如果不使用它,实际上是在声明一个全局变量!

函数f(){ n=;} f();Alert('n不是由f1函数中的var声明的,因此n是一个全局变量。\ r \验证n=' n ',window.n==n的结果为:“(window . n==n));运行结果:

二、如何从外部读取局部变量?

出于各种原因,我们有时需要在函数中获取局部变量。但是,如前所述,在正常情况下,这是不可能的,只能通过替代方法来实现。

也就是说,在函数内部,定义另一个函数。

函数f(){ var n=;//f函数内部的局部变量n//在f函数内部定义一个f函数f(){//f函数内部是可以访问局部变量n的alert(n);//}}在上面的代码中,函数f2包含在函数f1中,f1中的所有局部变量对f2都是可见的。但是反过来,f2内部的局部变量对f1是不可见的。这是Javascript语言特有的‘链作用域’结构。子对象将逐级查找父对象的所有变量。因此,父对象的所有变量对于子对象都是可见的,否则就不是真的。既然f2可以读取f1中的局部变量,那么只要取f2作为返回值,我们就可以在f1之外读取它的内部变量!有人可能会好奇,f2是函数,怎么能作为f1函数的返回值返回呢?其实是有可能的。JavaScript中的函数名本身就是一个变量,所以函数也可以作为普通变量使用。也就是说,不仅可以像传递参数一样将一个函数传递给另一个函数,还可以将一个函数作为另一个函数的返回值返回。

函数f(){ var n=;//局部变量n//f函数f(){alert(n)内部声明的f函数;}返回f;//取f函数作为f函数的返回值} var result=f();//f调用后返回值为f函数,结果为f函数结果();//999,调用f2函数运行结果:

第三,封闭的概念。

前面代码中的f2函数是闭包。各种专业文献中对‘闭包’的定义都很抽象,比如有这样一个闭包定义:JavaScript闭包就是把它从前一个函数或作用域得到的变量的副本保存在另一个作用域中,这些变量不会随着前一个函数的执行完成而被破坏’,这让我很难理解。我的理解是闭包是可以读取其他函数内部变量的函数。在Javascript语言中,只有函数内部的子函数可以读取局部变量,所以闭包可以简单地理解为“函数内部定义的函数”。因此,本质上,闭包是连接函数内部和外部的桥梁。

第四,使用闭包。

闭包可以在很多地方使用。它最大的用处有两个,一个是可以读取上面提到的函数内部的变量,另一个是可以将这些变量的值一直保存在内存中。

这句话怎么理解?请看下面的代码。

函数f(){ var n=;//nAdd是一个没有用var声明的全局变量,这个变量现在指向一个匿名函数NadD=function(){ n=} function f(){ alert(n)在f function内部声明;}返回f;}var结果=f();//result是f函数的结果();//第一次调用结果函数NaDD();//nAdd代表f函数内部声明的匿名函数,nAdd()正在调用匿名函数result();//第二次调用结果函数1000:

在这段代码中,结果实际上是闭包f2函数。一共运行了两次,第一次的值是999,第二次的值是1000。这证明函数f1中的局部变量n始终存储在内存中,并且在f1调用后不会自动清除。

为什么会这样?原因是f1是f2的父函数,f2赋给了一个全局变量,导致f2总是在内存中,而f2的存在依赖于f1,所以f1总是在内存中,调用后不会被垃圾回收回收。

这段代码中另一个值得注意的点是行“nAdd=function(){n=1}”。首先,在nAdd之前没有使用var关键字,所以nAdd是全局变量,不是局部变量。其次,nAdd的值是匿名函数,而匿名函数本身是闭包,所以nAdd相当于一个setter,可以对函数内部和函数外部的局部变量进行操作。

5.使用闭包的注意事项。

1)由于闭包可以将函数中的变量保存在内存中,这样会消耗大量的内存,所以我们不应该滥用闭包,否则会造成网页的性能问题,可能会造成IE中的内存泄漏。解决方法是在退出函数之前删除所有未使用的局部变量。

2)闭包将在父函数之外,并改变父函数内部变量的值。因此,如果将父函数用作对象,将闭包用作其公共方法,将内部变量用作其私有值,则必须注意不要随意更改父函数的内部变量的值。

第六,思考问题。

如果你能理解下面两个代码的运行结果,你应该能理解闭包的运行机制。

代码片段一:

变量名称=“窗口”;var object={name : 'My Object ',getname func : function(){ return function(){ return this . name;};}};alert(object . GetNameFunc());运行结果:

代码片段2:

变量名称=“窗口”;var对象={name : 'My Object ',getNameFunc : function(){var那=这个;return function(){ return . name;};}};alert(object . GetNameFunc());运行结果:

下面的注释被添加到代码中,以分析上面两个代码片段的运行结果:

代码片段一:

分析如下:

/*在JavaScript中,我们声明的JavaScript全局对象、全局函数和全局变量自动成为窗口对象的成员。全局变量是窗口对象的属性。全局函数是窗口对象的方法。*/变量名称=“窗口”;//声明一个全局变量名,它将自动成为窗口对象的一个属性。//证明:预警(' window . name:' window . name ');//可以用window.name(对象名)的形式访问name。属性名),这证明全局变量名自动成为窗口对象的属性。//声明全局对象对象。此时,全局变量对象自动成为窗口对象var对象={name : 'My Object '的属性。//对象的属性name getnamenfunc : function(){//对象的getnamenfunc函数的返回值//对象的getnamenfunc方法是匿名函数return function(){//这个此时指的是哪个对象,哪个对象指的是window对象,哪个对象调用的是这个所属的函数,这个指的是哪个对象。//证明这个in anonymous函数代表的是window对象而不是objectalert(结果‘this==object为’(this==object));alert(this==window的结果是:“(this==window));返回this.name//因为这代表窗口对象,所以this.name自然访问窗口对象的名称“The Window”};}};//证明全局对象是窗口对象的属性告警(' window . object:' window . object ');alert(' window . object . name:' window . object . name ');/*调用getNameFunc方法后,返回一个匿名方法。此时,retFn代表匿名方法,相当于给匿名方法起了一个名字叫retFn。此时,retFn函数自动成为window对象的函数*/var retFn=object . getname func();alert(retFn());//调用返回的匿名方法,那么是谁在调用这个匿名方法呢?是窗口对象//proof:retFn函数是窗口对象的函数alert(' window . retFn():' window . retFn());//可以用window.retFn()的形式调用retFn方法(对象名。方法名),则证明retFn函数是窗口对象2的函数代码片段:

分析如下:

变量名称=“窗口”;//全局变量名//全局对象objectvarobject={ name : ' my object ',getnamefunc3360function () {/*此时这代表哪个对象,此时这代表哪个对象,哪个对象调用这个函数。这指的是哪个对象已经执行完了那个=这个,然后那个也代表对象对象*/var那个=这个;//那是getNameFunc函数中声明的局部变量//证明这在getNameFunc函数中代表的是object而不是windowalert(结果‘this==object is’:(this==object));alert(this==window的结果是:“(this==window));//证明那个代表对象alert(结果‘that==object is:“(that==object));返回函数(){/*是在getNameFunc函数中声明的局部变量。正常情况下,getNameFunc函数调用完成后,那个局部变量会被JavaScript的GC回收,释放那个局部变量占用的内存空间,但是现在那个可以正常使用,还没有回收。原因是getNameFunc是这个匿名函数的父函数,调用getNameFunc后会返回并赋给一个全局变量retFn,导致内存中始终存在匿名函数,而匿名函数的存在依赖于getNameFunc,所以getNameFunc始终在内存中,调用结束后不会被垃圾回收回收。因为getNameFunc函数总是在内存中,所以在getNameFunc函数中声明的局部变量将总是存在于内存中。既然存在,就可以连续使用。*/返回该. name;//那代表对象,所以那个. name访问的自然是对象的名称‘我的对象’};}};var retFn=object . GetNameFunc();//调用getNameFunc方法后,返回一个匿名方法。此时,retFn代表匿名方法,相当于给匿名方法起了个名字叫retFnalert(retFn());最后,附上我在研究JavaScript闭包时写的一个例子:

脚本类型='text/javascript '函数A(){ var I=;//声明a函数内部的局部变量i//声明a函数内部的子函数b function b(){ alert(' I='(I));//访问局部变量I }返回子函数b内部的a函数内部声明的b;//返回函数b}的地址//*执行var c=A()后,变量c实际指向函数b,其中使用了变量I。执行c()后,会弹出一个窗口显示I的值(第一次是)。这段代码实际上创建了一个闭包,因为函数a外面的变量c指的是函数a里面的函数b,也就是说,当函数a在里面的时候。创建了所谓的“闭包”闭包。闭包的作用是A完成执行并返回后,闭包使JavaScript的垃圾收集机制GC不回收A占用的资源,因为A的内部函数B的执行依赖于A中的变量*/A();//这个时候,记忆中绝对会有我的空间。执行A()后,GC将回收为I var c=A()分配的内存空间;//在这种用法中,GC不会把I当作垃圾,处理掉c();//相当于调用b(),结果是:I=c();//结果是:I=c();//结果是:I=/脚本运行结果:

以上内容与边肖介绍的Javascript知识点总结(XVI)中对Javascript Closure代码的详细讲解有关,希望对大家有所帮助!

版权声明:JavaScript知识点总结(十六)Javascript闭包代码详解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。