JavaScript之闭包
什么是闭包?
先看一段代码:
1 | function a() { |
简单吧。再来看一段代码:
1 | function a() { |
简单吧。
什么是闭包?这就是闭包!
作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
看个🌰:
1 | function outerFun() |
再看个🌰:
1 | function outerFun() |
有权访问另一个函数作用域内变量的函数都是闭包。这里inc函数访问了构造函数a里面的变量n,所以形成了一个闭包。
再来看一段代码:
1 | function a() { |
看看是怎么执行的:
var c = couter(), 这一句couter()返回的是函数inc,那这句等同于var c = inc;
c(), 这一句等同于inc();
注意:函数名只是一个标识(指向函数的指针),而()才是执行函数
后面的三句翻译过来就是:var c = inc; inc(); inc(); 和第一段代码没有区别。
同时在上面的代码中,函数inc就被包括在函数a内部,这时a内部的所有局部变量,对inc都是可见的。但是反过来就不行,inc内部的局部变量,对a就是不可见的。这就是Javascript语言特有的“链式作用域”结构(chain scope),
子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然inc可以读取a中的局部变量,那么只要把inc作为返回值,我们不就可以在a外部读取它的内部变量了吗!
为啥要这样写?
我们知道,js每个函数都是一个个小黑屋,它可以获取外界信息,但是外界却无法直接看到里面的内容。将变量n放进小黑屋里,除了inc函数之外,没有其他办法能接触到变量n,而且在函数a外定义同名的变量n也是互不影响的,这就是所谓的增强“封装性”。
而之所以要用return返回函数标识inc,是因为在a函数外部无法直接调用inc函数,所以return inc与外部联系起来,代码2中的this也是将inc与外部联系起来而已。
常见的陷阱
看看这个:
1 | function createFunctions() { |
咋一看,以为是输出0~9,万万没想到输出10个10?
这里的错陷阱就是:函数带()才是执行函数!单纯的一句var f = function(){alert(“hi)};是不会弹窗的 ,后面接一句f()才会执行函数内部的代码,上面代码的翻译一下就是:
1 | var result = new Array(), i; |
为什么只垃圾回收了result,但不回收i?因为i还在被function引用着啊。好比一个餐厅,盘子总是有限的,所以服务员会去巡台回收盘子,但装着菜的盘子是不敢收的。当然自己手动到了那就可以回收了( =null ),这就是所谓的内存回收机制。
总结一下
闭包就是一个函数引用另一个函数的变量,因为变量被引用着所以不被回收,因此可以用来封装一个私有变量,这既是优点也是缺点,不必要的闭包只会徒增内存的消耗!另外使用闭包也要注意变量的值是否符合你的要求,因为他就像一个静态私有变量一样。