JavaScript 闭包究竟是什么
引言:理论基础
闭包就是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),可以读取函数外部的变量(其他函数内部变量),让这些执行环境始终保持在内存中。由于在javascript语言中,只有函数内部的子函数可以读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”,从而可以连接内部函数和外部函数。
也就是说,闭包是有权访问另一个函数作用域的变量的函数,创建一个闭包最直接的方式就是在函数内创建另一个函数,如内部函数。本质上所有函数都是闭包函数。
function test(){
for(var i = 0; i < 10 ; i++){
setTimeout(function(){
console.log(i)
}, 0);
}
}
test(); // 10 10 10 10 10 <span style="font-family: Arial;">10 10 10 10 10</span>
function test(){
for(var i = 0; i < 10 ; i++){
(function(li){
setTimeout(function(){
console.log(li)
}, 0);
})(i);
}
}
test(); // 0...9(0到9)
闭包有如下明显优点:能够读取函数内部的变量,并且让这些变量的值始终保存在内存中。使用闭包和匿名自执行函数实现模块化。
闭包也有如下明显缺点
1.既然不释放内存,则必然会对内存的消耗很大,造成页面访问的性能问题。解决办法:将不使用的局部变量全部删掉,当你退出函数时。
2.闭包会改变父函数的私有属性,在你不经意的时候
再举一个例子
function f1(){
var n=999;
nAdd=function(){n+=1}//定义了一个全局变量,相当于setter,方便在函数外部对函数内部的变量进行操作,不是本文要说明的重点
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000,可见变量保存在了内存中;
var result2 = f1();
result2();//999 这里的值为什么不是1000呢?带着疑问往下看
首先,我们要了解什么事内部函数:内部函数就是定义在另一个函数中的函数。例如:
function outerFn () {
function innerFn () {}
}
outerFn();//可以这么调用
innerFn();//不能这么直接调用内部函数
一个函数当没有依赖关系存在,就会有被垃圾回收机制回收的可能,但是如果像上文案例f1()那样将内部函数作为外部函数返回值赋值给一个全局变量,则会改变这种潜在的关系。这种即使离开函数作用域的情况下仍然能够通过引用调用内部函数的事实,意味着只要存在调用内部函数的可能,JavaScript就需要保留被该内部函数引用的函数(本文中就是outerFn)。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间(将内部函数置为null)。
2 )其他
闭包的一些经典题目或错误
1) javascript事件绑定
页面上有若干个div, 我们想给它们绑定一个onclick方法,于是有了下面的代码
<div id="divTest">
<span>0</span> <span>1</span> <span>2</span> <span>3</span>
</div>
<div id="divTest2">
<span>0</span> <span>1</span> <span>2</span> <span>3</span>
</div>
$(document).ready(function() {
var spans = $("#divTest span");
for (var i = 0; i < spans.length; i++) {
spans[i].onclick = function() {
alert(i);
}
}
});
很简单的功能可是却偏偏出错了,每次alert出的值都是4,简单的修改就好使了
var spans2 = $("#divTest2 span");
$(document).ready(function() {
for (var i = 0; i < spans2.length; i++) {
(function(num) {
spans2[i].onclick = function() {
alert(num);
}
})(i);
}
});
解释:上面代码在页面加载后就会执行,当i的值为4的时候,判断条件不成立,for循环执行完毕,但是因为每个span的onclick方法这时候为内部函数,所以 i 被闭包引用,内存不能被销毁,i的值会一直保持4,直到程序改变它或者所有的onclick函数销毁(主动把函数赋为null或者页面卸载)时才会被回收。这样每次我们点击span的时候,onclick函数会查找i的值(作用域链是引用方式),一查等于4,然后就alert给我们了。而第二种方式是使用了一个立即执行的函数又创建了一层闭包,函数声明放在括号内就变成了表达式,后面再加上括号括号就是调用了,这时候把i当参数传入,函数立即执行,num保存每次i的值。
白了个白~

590

被折叠的 条评论
为什么被折叠?



