JS 闭包
闭包
mdn上定义是:函数和声明该函数的词法环境的组合。
维基定义是:闭包 词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外
支持闭包的语言中,通常函数会是一等公民。也就是函数可以像普通变量一样使用。
相信你没有接触过的话读完只会更困惑。
我们知道JS是支持闭包的,下面看一个JS的🌰。
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo();
该例子中:函数checkscope内仍有一个函数f,而且f内引用了一个外部变量scope,其定义在checkscope中,但使用在f中。在调用时,分了两步:第一步执行checkscope(),其执行结束后有什么结果呢?答案是scope变量并未销毁,因为f函数仍然引用了它。故第二步执行foo()才没有问题。
这就是闭包的形式。
语言对比
像上面的这种函数套函数的写法,就是闭包的形式之一。有许多语言支持闭包,比如python,lua。并不是所有语言呢都支持这样,比如c语言,我们看一下c语言的函数,假如如下:
void func1(){
printf("http://c.biancheng.net");
void func2(){
printf("C语言小白变怪兽");
}
}
上述定义是❌的,因为C语言的函数不能嵌套定义。
闭包理解
闭包允许内层函数引用父函数中的变量。注意这里说的是对外层的引用。
示例:
/**
* <body>
* <ul>
* <li>one</li>
* <li>two</li>
* <li>three</li>
* <li>one</li>
* </ul>
*/
var lists = document.getElementsByTagName('li');
for(var i = 0 , len = lists.length ; i < len ; i++){
lists[ i ].onmouseover = function(){
alert(i);
};
}
这里鼠标移过每个li元素,总是弹出4。
为什么呢?
首先在匿名函数( function(){ alert(i); })内部查找是否定义了 i,结果是没有定义;因此它会向上查找,查找结果是已经定义了,并且i的值是4(循环后的i值);所以,最终每次弹出的都是4。
其中一种解决办法是:
var lists = document.getElementsByTagName('li');
for(var i = 0 , len = lists.length ; i < len ; i++){
(function(index){
lists[ index ].onmouseover = function(){
alert(index);
};
})(i);
}
内存泄漏
闭包十分容易造成浏览器的内存泄露。JS采用的是一种自动垃圾回收机制。当一个对象无用的时候,即程序中无变量引用这个对象时,就会从内存中释放掉这个变量。
循环引用
A->B->C->B :这里增加了C的某一属性引用B对象,如果这是清除A,那么B、C不会被释放,因为B和C之间产生了循环引用。
闭包非常容易创建循环引用,故有可能导致内存泄漏。