在理解了作用域和看了诸多如阮一峰大神的博客后之后,闭包还是花了很长时间去理解。
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
1 | function f1(){ |
什么是闭包?
上面代码中的f2函数,就是闭包。
各种专业文献上的”闭包”(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数(f1)内部变量的函数(f2)。
在js中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数(f1)内部的函数(f2)”。
所以,在本质上,闭包就是将函数(f1)内部和函数外部(f1外部)连接起来的一座桥梁。在js中,所有的function都是一个闭包
闭包的用途 ## (#闭包的用途)
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,即维持一个变量。
闭包能够通过保护函数内的变量安全(即函数的变量只能由其内部函数访问,不能被其外部访问)实现JS私有属性和私有方法;
1 | function f1(){ |
- f2被赋予result,成为了全局变量,所以f2始终在内存中,而f2作用域链上依赖f1,所以导致f1也始终在内存中,在f1被引用后,不会被垃圾回收机制(garbage collection)回收;
- var result=f1()这里引用了f1();n的值为999,并一直在内存中,运行result()即f2();结果999;nAdd()后,n=n+1=1000;再次执行result(),结果1000;
这就是这些函数内部的变量的值始终保持在内存中。
使用闭包的注意点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
1 | function outerFun() { |
知道了这些,js的垃圾回收机制(GC)好懂多了! ## (#知道了这些js的垃圾回收机制gc好懂多了)
在Javascript中,
如果一个对象不再被引用,那么这个对象就会被GC回收。
如果两个对象互相引用,而不再被第3者所引用,那么,引用之后这两个互相引用的对象也会被回收。就像是:
1 | function outer(){ |
- 如果函数a被b引用,b又被a外的c引用(闭包),则函数a执行后不会被回收的原因。
除此之外
把闭包讲的很深入的一篇博客 (target=undefined),还讲了函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。并以函数a从定义到执行的过程为例阐述这几个概念。