什么是闭包?
通俗的说法是对象是变量带着函数,而闭包是函数带着变量。但是在理解闭包之前估计也是很难理解这通俗的说法。还有一种观点说是 Javascript种所有发函数都是闭包。 不去探讨这个观点的正确与否,从这两者的说法中我们可以看到闭包一定是与函数有关,在Javascript中函数就是一块作用域,也就是说闭包是与作用域有关的。
在Javascript中,函数是一种特殊的对象,他能在其他函数里面声明,他可以被赋值给变量,他特殊的地方是可以被调用。在C/C++中,函数必须有声明,然后可以被调用,函数调用时会切换到一个新的上下文环境中执行代码。在Javascript中不是这样的,一个函数可以不被声明立即执行,函数执行的时候也不会切换上下文环境。
那么在Javascript中作用域是怎么样的呢?一个函数就类似于其他语言中的一对{},{}里面可以访问{}外面的变量,在函数里可以访问函数外的变量。在其他语言中函数无法访问外部函数的变量,只可以访问局部变量以及全局变量。Javascript中特殊的地方是函数可以访问外部函数的变量。
那么闭包是什么呢?无论用什么方法让一个内部函数(这个函数在别的函数内部)在外面执行,他会获得最原始声明处和当前执行处的作用域。
所以有人说闭包是可以读取其他函数内部的变量,这里的其他函数就是这个函数原始声明处的外部函数。
看看闭包
1 | var a = 2; |
为什么所有函数都是闭包呢?因为函数一定是声明在某一个上下文环境当中的,上面这个f1就是声明在全局环境中。f1的执行环境也是这个全局环境,和其他语言一样,函数内部访问到a并不令人感到意外,虽然结果是一致的,但是原因可能和你想象的不大一样。
1 | function f1(){ |
虽然可能与其他语言中有一些不一样,但是这并不意外嘛!
1 | function f1(){ |
这个看起来有点意外,f1执行完之后返回了一个函数之后没有被释放吗?为什么f2执行的时候不会报错?这个就是闭包。无论它在哪里执行,它具有声明和执行时候的作用域。f2虽然在全局环境中执行,但是声明是在f1内部,所以f2之后执行的时候依然能够访问到f1内部变量a
。
1 | function f2(){ |
这个结果是多少呢?f2在全局环境中声明, 在f1内部执行。也就说说f2执行的时候f2具有全局作用域和f1作用域。那么哪个作用域优先呢?试一试!
1 | function f2(){ |
那么这一段代码运行的结果是多少呢?为什么呢?理解了吗?
闭包的作用
首先是变量的封装隐藏。你看上面的例子,一个外部函数的返回值是一个函数。这个函数可以访问外部函数的变量,而没有其他办法可以访问这个外部函数的变量,实现了人为的封装,private性。
第二个作用是实现类似static count的效果,在内存中一直维持着这个变量。
缺点就是闭包锁住了这个环境,查找作用域的时候会带来额外的开销,并且内存的开销也会变大。