先明确javascript的两个特性:
- javascript具有特殊的作用域链特性,内部作用域可以访问外部作用域的变量
- JavaScript的垃圾回收机制,即标记清除。当函数执行完毕时,它的活动对象会从内存中被清除。
《JavaScript高级程序设计》中对闭包的定义是:有权访问另一个函数作用域中的变量的函数。由于内部作用域可以访问外部作用域,所以理论上所有定义在另一个函数内部的函数都是一个闭包。但是函数函数执行完了或return了之后它的活动对象(包括它内部的闭包)就会被清除掉,这个“包”就没了。
当我们想要从外部作用域访问内部变量或者想让函数内部变量保存在内存中该怎么办呢?
想要把这个包存起来就得把它赋给一个外部(这个外部是指父函数的外部)的变量(通常是全局变量,全局变量不会被清除)。
下面第一个例子中,把闭包函数作为返回值赋给了全局变量add,所以闭包得以保存。
第二个例子,把闭包赋给了this的pubicMethod属性,这个this一般是调用构造函数MyObject生成的一个实例对象,这个实例在构造函数外部存活,所以闭包也可以在外部存活,可以通过实例的pubicMethod属性访问到。而privateFunction虽然也是定义在函数内部的函数,可以访问父函数的变量,但是父函数执行完后,它会被回收掉,外部作用域是无法访问到它的。
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
//这是一个赋值语句,首先右边的匿名函数进行了一个自调用,得到一个返回值,这个返回值是一个函数,即function () {return counter += 1;},
//我们把这个函数叫做子函数。然后把这个返回值也就是这个子函数赋给了add.所以此时的add其实是一个函数。而此时add还没有被调用过,所以counter为0
add(); //第一次调用 即function () {return counter += 1;}调用一次,counter的值变为1
add();//第二次调用,add做为子函数可以访问父函数作用域内的变量,也就是counter,其值此时为1 调用子函数add后变为2.
add();//第三次调用,counter变为3
闭包的优点:闭包使得函数可以向外部暴露一个获取和操作函数内部变量的接口,这个接口也就是内部函数,由于内部函数是定义好了的,所以外部环境只能按照这个内部函数的规则操作内部变量,而不是随意修改,这样就起到了保护私有变量的作用。并且内部变量名不会污染全局命名空间。
闭包的缺点:每执行一次外部函数就会在内存中保存一个闭包,如果不及时清除对闭包的引用就会造成大量的空间浪费,也就是内存泄漏。