Javascript的一个特殊点就在于它的闭包和回调特性,这两个特性让初学Javascript的我是云里雾里,至今仍在苦苦摸索与理解。在一番苦思之后,整理了一下资料,将自己的理解思路记录下来,以免今后糊涂的时候再次作为索引,也希望能给和我同样的初学者带来理解上的一点帮助。
要清楚地理解闭包,首先要有如下几个概念需要清楚:
作用域:
作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
一切解释都没有例子来的清楚明白:
function say_hello(){
var words = "hello world!";
alert(words);
}
这里变量words的作用域就是括号“{”和“}”之间的范围。
作用链域:
简单来说就是如果函数中需要用到的参数在本函数中有,则用本函数中的参数,如果没有则查询上一级函数中是否有,有则用之,没有则再次向上一级查询,直至找到或找不到为止(从最内层到最外层都没有)。这里不再多说,详细的说明见:Javascript中的作用链域。
继续看例子:
var str = "outer";
function outerFun(){
var str = "inner";
alert("------1------"+str);
function innerFun(){
str +="_new";
alert("------2------"+str);
}
innerFun();
alert("------3------"+str);
}
outerFun();
alert("------4------"+str);
//输出结果
------4------outer
------1------inner
------2------inner_new
------3------inner_new
这里可以看出函数outerFun内定义了str,但是最后的alert出的str却是“------4------outer",这说明外面是访问不到里面的变量,就是outerFun函数中的变量str的作用域在outerFun函数的花括号之间;但innerFun函数中
却可以访问外面outerFun中的str,这就是作用链域的作用了,即,本作用域内不存在的变量,就向上一级进行查找,如果该例子中outerFun中没有定义str,
那么就会是如下的情形:
var str = "outer";
function outerFun(){
alert("------1------"+str);
function innerFun(){
str +="_new";
alert("------2------"+str);
}
innerFun();
alert("------3------"+str);
}
outerFun();
alert("------4------"+str);
//输出结果
------4------outer
------1------outer
------2------outer_new
------3------outer_new
js的内存回收机制:
一般一个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了,对应的内存空间也就被回收了。下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用。
但是,在Javascript中存在闭包的情形,所以回收机制会有点特殊。
闭包:
所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
这个定义十分的难以理解,远远的丢至一边,还是用简单的话解释一遍:多层嵌套的函数中,内层的函数能够访问包含它(或是它的上级)的函数的所有变量,同时能够通过回调使得其他作用域也可以使用这些变量。其实这一点就是作用链域的体现,这种特定的变量访问的方式我们就理解为Javascript的闭包特性。
匿名函数:
简单来讲,一句话:没有函数名的函数。
var add = function(x,y){return x+y;}
上句中右边就是一个匿名函数。同样,这是另一种定义方式。
(function(x,y){
alert(x+y);
})(3,5);
匿名函数与闭包的区别:由于见到匿名方式的闭包比较多,因此很多人将匿名函数和闭包理解为一个概念,其实要判别一个匿名函数是不是闭包,关键就是看是否通过回调将该函数所属函数中的变量传递给了其他的作用域。
顺便提一个《Javascript高级程序设计》中的闭包的一个例子,对于初学者来说可能相对较难理解(我是用了非常久才想明白的):
function createFunctions(){
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function(){
return i;
}
}
return result;
}
闭包内是对变量的引用,而非变量值的副本,也就是说result数组的每一项的值都是对变量i的引用,仅仅只是相当于一个指针,当i的值改变时,自然result的每一项的值也就随之改变,该例子中循环完之后i为10,所以每一项都是10。(其实还是比较好理解的哈,我当初不能理解【就是看了说明还是没能理解】的原因是没有把return
后的 i 当成是引用,而是将其当成值赋给了result数组...)
后的 i 当成是引用,而是将其当成值赋给了result数组...)
所以,理解之后就很好改了:
function createFunctions(){
var result = new Array();
for(var i=0;i<10;i++){
result[i] = (function(num){
return num;
})(i);
}
return result;
};
闭包内如何赋值给全局变量:
这是今天最初的目标,顺便借这个机会,将这些都整理一遍,加深理解。最后解答一下这个简单问题,直接上例子:
<div id="info">
<ul></ul>
</div> function info(){
var strArr = [];
var data = [
{
"name":"Anny",
"age":"21",
"tel":"333333"
},
{
"name":"Bob",
"age":"33",
"tel":"232223"
},
{
"name":"Tom",
"age":"25",
"tel":"53344"
}
]
for(var i=0;i<data.length;i++){
strArr[i] = (function(obj){
return "姓名:"+obj.name+" 年龄:"+obj.age+" 电话:"+obj.tel;
})(data[i]);
}
return strArr;
}
var strArr = info();
$("#info ul").html("<li>"+strArr[0]+"</li><li>"+strArr[1]+"</li><li>"+strArr[2]+"</li>");