闭包是函数和声明该函数的词法环境的组合。是一个能够访问另一个函数作用域中的变量的函数。
看下面的例子:
displayName()是定义在makeFunc()里的内部函数,仅在该函数体内可用。displayName()内没有自己的局部变量,然而它可以访问到外部函数的变量,所以name是有值的。
再来看下面的例子。
这段代码的运行效果和之前的是一致的,但在内部函数displayName()执行前,被外部的函数给返回了。
因为在一些其他一些编程语言中,函数中的局部变量仅在函数的执行期间可用。一旦makeFunc()执行完毕,我们会认为name变量将不能被访问。但此代码运行结果无误。
原理:JavaScript中的函数会形成闭包。闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建所能访问的所有局部变量。myFunc 是执行 makeFunc 时创建的 displayName 函数实例的引用(可以理解为myFunc就代表着函数内部displayName),而displayName 实例仍可访问其词法作用域中的变量,即可以访问到 name 。
再来看一段代码
add5(2)中的2就代表着yadd5 和 add10 都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5 的环境中,x 为 5。而在 add10 中,x 则为 10。
用闭包模拟私有方法
下面的示例展现了如何使用闭包来定义公共函数,并令其可以访问私有函数和变量。这个方式也称为模块模式(module pattern)
这次我们只创建了一个词法环境,为三个函数所共享:Counter.increment、Counter.decrement、Counter.value。该共享环境创建于一个立即执行的匿名函数体内,这个环境中包含两个私有项:privateCounter变量和changeBy函数。
下面的例子我们将这个匿名函数保存到另外一个变量makeCounter中,不立即调用。
请注意两个计数器 Counter1 和 Counter2 是如何维护它们各自的独立性的。每个闭包都是引用自己词法作用域内的变量 privateCounter 。
每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境。然而在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。
在ES6还没引入let关键字之前,在循环中有一个常见的闭包创建问题。
因为在触发onfocus事件之前,for循环都已经走完了,所以无论点击哪一个input,p标签里面的内容都是age的描述。变量对象item(被三个闭包所共享)已经指向了helpText的最后一项。而改变这一问题就要使用闭包(closure)。
这段代码可以如我们所期望的那样工作。所有的回调不再共享同一个环境, makeHelpCallback 函数为每一个回调创建一个新的词法环境。在这些环境中,help 指向 helpText 数组中对应的字符串。
也可以使用匿名函数自调用闭包
为了避免使用更多的闭包,可以直接将循环中的var改成let
无敌之终极闭包面试题:
答案:
我对这道题的总结:第一次的m和n将作为下一次n和o的值,而a.fun()是改变m的值,而fun()是改变n的值。综合起来
优点:避免污染全局空间,少定义一些全局变量。
缺点:没有让浏览器的垃圾回收机制(标记清除、引用计数)回收局部声明的变量,占用大量内存。
全文内容来自:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
网友评论