JavaScript的闭包是一个特色,但也是很多新手难以理解的地方,阅读过不少大作,对闭包讲解不一,个人以为,在《JavaScript高级程序设计》一书中,解释的最为详尽,结合此书,表述一下我对JavaScript闭包的理解,希望能对新手有些帮助。
闭包的例子
var count = 10; //全局作用域 标记为flag1
function add() {
var count = 0; //函数全局作用域 标记为flag2
return function() {
count += 1; //函数的内部作用域
alert(count);
}
}
var s = add()
s(); //输出1
s(); //输出2
复制代码
来看一下发生了什么吧,add()的返回值是一个函数,首先第一次调用s()的时候,是执行add()的返回的函数,也就是下面这个函数:
function(){
count+=1;//函数的内部作用域
alert(count);
}
复制代码
也就是将count+1,在输出,那count是从哪儿来的的呢,根据作用域链的规则,底层作用域没有声明的变量,会向上一级找,找到就返回,没找到就一直找,直到window的变量,没有就返回undefined。这里明显count 是函数内部的flag2 的那个count ,
var count=10;//全局作用域
function add(){
//var count=0;注释掉了
return function(){
count+=1;//函数的内部作用域
alert(count);
}
}
var s=add()
s();//输出11
s();//输出12
复制代码
自然这是体现不出闭包的性质,只为了说明函数作用域链
继续说明:第一次执行,是没有疑问的输出1,那第二次的过程是怎样的呢?
继续执行那个函数的返回的方法,还是count+=1;然后再输出count ,这里问题就来了,不应该继续向上寻找,找到count=0;然后输出1吗?不知道有没有注意一个问题,那就是s()执行的是下面这个函数
function(){
count+=1;//函数的内部作用域
alert(count);
}
复制代码
而不是
function add(){
var count=0;//函数全局作用域 标记为flag2
return function(){
count+=1;//函数的内部作用域
alert(count);
}
}
复制代码
也就是说add(),只被执行了一次。然后执行两次s(),那count的值就是只声明了一次。
var s=add(),函数add 只在这里执行了一次。
下面执行的都是s(),那第二次的count的值是从哪儿来的,没错它还是第一次执行add时,留下来的那个变量。
(这怎么可能,函数变量执行完就会被释放啊,为什么还在?这里就是一个垃圾回收机制的引用计数问题)。
“”如果一个变量的引用不为0,那么他不会被垃圾回收机制回收,引用,就是被调用“”。
由于再次执行s()的时候,再次引用了第一次add()产生的变量count ,所以count没有被释放,第一次s(),count 的值为1,第二次执行s(),count的值再加1,自然就是2了。
让我们返回来再看看,根据以上所说,如果执行两次add() ,那就应该输出 都是1,来改一下这个函数
function add(){
var count=0;//函数全局作用域
return function(){
count+=1;//函数的内部作用域
alert(count);
}
}
add()();//输出1
add()();//输出1
复制代码
输出的两次都是1. 不知道通过这个示例,你有没有理解了闭包。 描述一下闭包的结构吧,为什么闭包一般都需要一个匿名函数,为了实现作用域链的规则,需要有两层作用域。 想来大家都应该理解了。 下面再描述一个常见的错误
<p>1</p><p>2</p><p>3</p>
<p>4</p><p>5</p><p>6</p>
var plist=document.getElementsByTagName('p');
for (var i=0;i<plist.length;i++) {
plist[i].onclick=function(){
alert(plist[i].innerHTML)//全是undefined
}
}
复制代码
想要点击相应的p 弹出对应的i的值,但是这里发生了什么,点击任意一个数,弹出的都是undefined,我的天,不应该是1,2,3,4,5,6的吗? 解释一下,函数执行完,i 的值是6 ,没有错吧。那plist[6]是不是undefined。 我们点击的时候,触发的就是输出undefined,但我们想要的值是点击对应的p 的innerHTML, alert(this.innerHTML)//万事大吉, this 是一个好东西,指向当前对象当我们绑定的时候绑定的就是当前值,而不是动态的i的值,前面绑定的是一个动态i 的值,这里也可以使用闭包解决,不过不推荐,毕竟闭包使用的话,会让内存无法释放,也就是闭包越多,占的内存越多。使用需谨慎。
总结一下
JavaScript闭包的形成原理是基于函数变量作用域链的规则 和 垃圾回收机制的引用计数规则。
JavaScript闭包的本质是内存泄漏,指定内存不释放。
(不过根据内存泄漏的定义是无法使用,无法回收来说,这不是内存泄漏,由于只是无法回收,但是可以使用,为了使用,不让系统回收)
JavaScript闭包的用处,私有变量,获取对应值等,。。