标签:
闭包是JavaScript一个精髓和难点所在,很久以前就开始了解它,总想写点什么,遗憾的能力有限,包括您现在看到的这篇文章,大多数也是来自其他优秀的书籍和少部分自己的理解,我将只充当一个搬运工的角色,好了不多说了,开始正题吧!
在了解闭包之前我们必须要先去深入了解JavaScript的作用域和作用域链。在JavaScript中作用域是在函数定义的时候决定的,而不是函数调用时决定的,举例说明。
var count=5; function test1(){ var count =0; function getCount(){ return count; } return getCount();
}
console.log(test1());//0
在上面的代码中我们最后得到的结果应该是0,原因很简单因为在执行getCount这个函数时,返回的会是局部变量count(这个可以去看一下JavaScript变量的查询规则)。那么再看下面这个例子。
var count=5; function test1(){ var count =0; function getCount(){ return count; } return getCount; } console.log(test1()());//0
在这段代码中,我们只做了一点改变,我们返回的是一个函数,而不是函数的执行结果,所以最后要变成test1()()。那么大家可以看到执行的结果依然是0。这就说明了函数的作用域是在函数定义时就已经确定了,而不是在函数调用时确定的。因为如果是在调用时确定,那么最后执行test1()()这个函数,实际上是执行getCount()这个函数,而这个函数中会反回一个count,在当前的位置,能够拿到的count应该是全局变量count=5,打印结果应该是5,但是最后证明是0,所以就证明了上面那句话:在JavaScript中作用域是在函数定义的时候决定的,而不是函数调用时决定的。这就是闭包,在作用域外依然能够访问到局部变量,并且一直保持下去而不被垃圾回收(一般情况下,局部变量会在函数执行完后,被当做垃圾回收,而在全局域是不可能访问到这个局部变量的)。
再举一个例子:
function test4(){ var count=3; return { getCount:function(){ return count; } } } console.log(test4().getCount());//3
我们在执行test4().getCount();这个语句时,执行环境是在全局域中,按理说我们是不可能拿到局部变量中的count值的,但是我们拿到了,这就是闭包的神奇之处,大家可能已经看到了,闭包中一定会return一个东西,可以直接是这个局部变量,也可以是获取这个局部变量的方法。那么闭包有什么用呢,其实我们可以用来保护某些变量,要想获取或者改变这个变量就必须通过我给定的方法来实现,有点类似于Java里面的setter和getter方法,下面我们来模拟一下。
function test5(){ var count=6; return { getCount:function(){ return count; }, setCount:function(val){ return count=val; } } } console.log(test5().getCount()); //6 console.log(test5().setCount(9)); //9 console.log(test5().setCount(9)); //9
以上就是模拟的getter和setter方法,一般情况下我们可能去定义一个全局变量来达到这种效果,但是大家都知道这样很不安全,而这种方法就很好的保证了count的安全性,不会被轻易的篡改,因为你不通过setCount这个方法你根本碰不了它。
特殊的例子,可能从里开始接触闭包开始就一定会碰到这个例子
function test2(){ var arr=[]; for(var i=0;i<10;i++){ arr[i]=function(){ return i; } } return arr; } console.log(test2()[5]());//10
上面的代码打印的结果是10,我们期待的结果应该是5。我们的本意应该是这个数组中,依次装入了0~9,但是最后我们发现,这个数组中装的全是10,为什么会这样呢。
当我们在执行arr[i]=function(){ return i;}时,并不是把i装入了数组中,而是把function(){return i;}这个函数装入了数组中,而这个函数并未有执行,怎么可能会returni呢!
而当我们最后执行test2()[5]()这句话时本意是要去除数组的第六个元素,在这时我们才会执行function(){return i;}这个函数(把test2()[5]替换成function(){return i;});而此时的i早已经变成了10,说以test2()[0]~test2()[9]的值都会是10,如果要达到我们的本意应该做如下改动:
function test3(){ var arr=[]; for(var i=0;i<10;i++){ arr[i]=(function(){ return i; }()) } return arr; } console.log(test2()[7]);//7
我们运用一个及时函数,使每次function(){return i;}这个匿名函数立马执行掉,那么arr[i]装入的值就是对应的i值了(关于立即执行函数,大家可以去查看其它资料,也是JavaScript中相当重要的部分)。类似的例子还有。
for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 1000); }
我们的本意是想在1s以后依次打印0~9这10个数字,而真实结果却是全打印的10。原因依然是function(){console.log(i);}这个匿名函数要在1s以后才会执行,不是立马执行,而到1S以后,i的值早已经变为了10,解决办法如下:
for(var i = 0; i < 10; i++) { (function(e) { setTimeout(function() { console.log(e); }, 1000); })(i); }
那么这样就会打印出0~9这10个数了。参见资料(JavaScript私密花园)。
好了关于闭包目前就只能写这么多了,大家可以看看《JavaScript权威指南》《你不知道的JavaScript》和《JavaScript语言精粹》中对闭包的解释,看了之后应该有个比较好的理解。
标签:
原文地址:http://www.cnblogs.com/djlxs/p/5407374.html