一:了解闭包首先要了解嵌套函数的词法作用域规则。
var scope = "global scope"; /*全局变量*/ function checkscope() { var scope = "local scope"; /*局部变量*/ function f() { return scope; /*在作用域中返回这个值*/ } return f(); } console.log(checkscope()); /*"loacl scope*/
checkscope()函数声明了一个局部变量,并定义了一个函数f(),函数f()返回了这个变量的值,最后将函数f()的执行结果返回。
改动一下代码,返回什么?
var scope = "global scope"; /*全局变量*/ function checkscope() { var scope = "local scope"; /*局部变量*/ function f() { return scope; /*在作用域中返回这个值*/ } return f; } console.log(checkscope()()); /*返回什么*/
在这段代码里,将函数内的一对圆括号移动到了checkscope()之后。checkscope()现在仅仅返回函数内嵌套的一个函数对象,而不是直接返回结果。在定义哈数的作用域外部,调用这个嵌套的函数。
二:实现闭包
uniqueInteger.counter = 0; function uniqueInteger() { return uniqueInteger.counter++; } console.log(uniqueInteger());
这个函数使用自身的一个属性来保存每次返回的值,以便每次调用都能跟踪上次的返返回值。但是这个做法有个问题,就是恶意代码可能将计数器重置或者把一个非整数付给他,导致函数不一定产生“唯一”的“整数”。而闭包能够捕捉到单个函数调用的局部变量,并将这些局部变量用作私有状态。
var uniqueInteger = ( function () { var counter = 0; return function () { return counter++ }; }() ); console.log(uniqueInteger());
这段代码定义了一个立即调用的函数(函数的开始带有左圆括号),因此是这个函数的返回值赋值给变量uniqueInteger。
这个函数返回另一个函数,这是一个嵌套函数,我们将它赋值给uniqueInteger,嵌套的函数是可以返回作用域内的变量的,而且可以访问外部函数中定义的counter变量。当外部函数返回以后,其他代码都无法访问counter变量,只有内部函数才能访问到他。
像counter一样的私有变量不是只能用在一个单独的闭包内,在同一个外部函数内定义的多个函数也可以访问它,这与多个嵌套函数都共享一个作用域链。
function counter() { var n = 0; return { count:function () { return n++; }, reset:function () { n = 0; } }; } /*创建2个计数器*/ var c = counter(); var d = counter(); console.log(c.count()); /*0*/ console.log(d.count()); /*0*/ console.log(c.reset()); /*重置了*/ console.log(c.count()); /*0:将c重置了*/ console.log(d.count()); /*1*/
counter()函数返回一个“计数器”对象,这个对象包含两个方法:count返回下一个整数,reset将计数器重置为内部状态。首先要理解,这两个方法都可以访问私有变量n。再者,每次调用counter()都会创建一个新的作用域链和一个新的私有变量。因此,如果调用counter()两次,则会得到两个计数器对象,而且彼此包含不同的私有变量,调用其中一个计数器对象的count和reset不会影响到另一个对象。