宝哥软件园

继续学习javascript闭包

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

1.什么是结束?

官方的解释是闭包是一个表达式(通常是一个函数),有很多变量和这些变量绑定的环境,所以这些变量也是表达式的一部分。我相信很少有人能直接理解这句话,因为他的描述太学术了。其实这句话是:JavaScript中的所有函数都是一个闭包。一般来说,嵌套函数产生的闭包更强大,大部分时候就是我们所说的“闭包”。请看下面的代码:

函数a(){ var I=0;函数b(){ alert(I);}返回b;} var c=a();c();该代码有两个特征:

1.函数b嵌套在函数a中;

2.函数a返回函数b。

参考关系如图所示:

这样,执行var c=a()后,变量c实际指向函数b,执行c()后,会弹出一个窗口,显示I(第一次为1)的值。这段代码实际上创建了一个闭包。为什么呢?函数a外的变量c指函数a内的函数b,即:

当函数A的内部函数B被函数A外部的变量引用时,就创建了闭包。

让我们说得更透彻些。所谓“闭包”,就是在构造函数体中定义另一个函数作为目标对象的方法函数,这个对象的方法函数反过来引用外部函数体中的临时变量。这使得可以间接维护原始构造函数使用的临时变量值,只要目标对象可以在其生存期内始终保持其方法。虽然初始构造函数调用已经结束,临时变量的名称已经消失,但是变量的值始终可以在目标对象的方法中引用,并且该值只能由该方法访问。即使再次调用同一个构造函数,也只会生成新的对象和方法,新的临时变量只对应新的值,与上次调用的值无关。

二、闭包的作用是什么?

简单来说,闭包的作用就是在A完成执行并返回后,闭包使得Javascript的垃圾收集机制GC不回收A占用的资源,因为A的内部函数B的执行依赖于A中的变量,这是对闭包的作用非常直白的描述,不专业也不精确,但很可能就是这个意思。理解闭包需要一个渐进的过程。

在上面的例子中,由于闭包的存在,函数a返回后我总是存在于a中,所以每次执行c()时,我都是alert加1后的值。

让我们想象另一种情况。如果A不返回函数B,情况就完全不同了。因为A执行后,B不返回A的外部世界,只被A引用,此时A只会被B使用,所以函数A和B相互引用,不受外部世界的干扰(被外部世界引用),函数A和B会被GC回收。(后面会详细介绍Javascript的垃圾收集机制。)

第三,封闭中的微观世界。

如果我们想更多地了解闭包以及函数A和嵌套函数B之间的关系,我们需要引入其他几个概念:函数执行上下文、调用对象、作用域和作用域链。以函数A从定义到执行的过程为例来说明这些概念。

定义函数A时,js解释器会将函数A的作用域链设置为定义A时A所在的“环境”,如果A是全局函数,则作用域链中只有一个窗口对象。当函数a被执行时,a将进入相应的执行上下文。在创建执行环境的过程中,首先给A增加一个作用域属性,即A的作用域,其值在步骤1中为作用域链。也就是a.scope=a的范围链.然后,执行环境创建一个调用对象。活动也是一个带有属性的对象,但是它没有原型,不能通过JavaScript代码直接访问。创建活动对象后,将活动对象添加到A的范围链的顶部。此时,的范围链包含两个对象:的活动对象和窗口对象。下一步是在活动对象上添加一个arguments属性,保存调用函数A时传递的参数,最后将函数A的所有形式参数和函数B的内部引用都添加到A的活动对象中,这一步完成了函数B的定义,所以和第三步一样,将函数B的作用域链设置到B定义的环境中,也就是A的作用域中,此时就完成了整个函数A从定义到执行的步骤。此时a返回函数b对c的引用,函数b的作用域链包含函数a的活动对象的引用,这意味着b可以访问a中定义的所有变量和函数.函数b被c引用,依赖于函数a,所以函数a返回后不会被GC回收。

当执行函数b时,它将与上述步骤相同。因此,b在执行过程中的范围链包括三个对象:b的活动对象、a的活动对象和窗口对象,如下图所示:

如图所示,当访问函数b中的变量时,搜索顺序为:

先搜索自己的活动对象,如果存在就返回;如果不存在,继续搜索函数a的活动对象,依次搜索,直到找到为止。如果函数b中有原型对象,在找到自己的活动对象后找到自己的原型对象,然后继续寻找。这是Javascript中的变量搜索机制。如果在整个范围链中找不到它,它将返回undefined。总之,这一段提到了两个重要的词:函数的定义和执行。在本文中,函数的范围是在定义函数时确定的,而不是在执行函数时确定的(参见步骤1和3)。用一段代码来说明这个问题:

函数f(x){ var g=function(){ return x;}返回g;} var h=f(1);警报(h());这段代码中的变量h指向f中的匿名函数(由g返回)。

假设函数h的作用域是通过执行alert(h())确定的,那么h的作用域链就是:h的活动对象-alert-the-window对象的活动对象。假设函数h的作用域是在定义的时候确定的,也就是说h所指向的匿名函数在定义的时候已经确定了它的作用域。在执行的时候,h的作用域链是:h的活动对象-f的活动对象-窗口对象。如果第一个假设成立,则输出值未定义;如果第二个假设成立,输出值为1。

结果证明第二个假设是正确的,这意味着函数的范围是在定义函数时确定的。

第四,闭包的应用场景保护函数中变量的安全。以初始例子为例,函数A中的I只能被函数B访问,而不能被其他手段访问,从而保护了I的安全性。

在内存中维护一个变量。和前面的例子一样,因为闭包的原因,函数A中的I总是存在于内存中,所以每次执行C()时,我都会被加1。通过保护变量的安全性,JS私有属性和私有方法(不能从外部访问)不能在构造函数外部访问。

函数构造函数(.){ var=this;var membername=value函数成员名(.) {.}}以上三点是闭包最基本的应用场景,很多经典案例都源于此。

动词(verb的缩写)Javascript的垃圾收集机制。

在Javascript中,如果一个对象不再被引用,它将被GC回收。如果两个对象相互引用,并且不再被第三个引用,那么这两个相互引用的对象也将被回收。因为函数A被B引用,而B在A之外被C引用,这就是函数A执行后不会被回收的原因。

不及物动词结论

了解JavaScript闭包的解释和操作机制,可以写出更安全、更优雅的代码,对大家的学习有帮助。

更多资讯
游戏推荐
更多+