一、书上的闭包概念
关于javascript闭包,首先我们来看一些书籍中所给的定义。
《javaScript权威指南》中的定义如下:
函数对象可以通过作用域链互相关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学中成为闭包。
《javaScript高级教程》中的定义如下:
闭包是指有权访问另一个函数作用域中的变量的函数。
二、变量的作用域
由此可见,想要理解闭包,我们首先必须理解javaScript变量的作用域。
而变量的作用域无非就是2种:全局变量和局部变量。
所谓的全局作用域就是函数外部的区域,而全局变量就是在函数外部所定义的变量。
如下代码,在函数(f1)内部可直接读取全局变量(age)。
//此处为全局作用域
var age=18;// 此为全局变量age
function f1(){// 此为函数f1
alert(age);
}
f1(); // 18
所谓的局部作用域就是函数内部的区域,而局部变量就是在函数内部所定义的变量。
如下代码,在函数(f1)外部无法读取函数内部的变量(age)。
function f1(){// 此为函数f1
// 此处为局部作用域
var age=18;// 此为局部变量age
}
alert(age);// error
注意:在定义局部变量的时候,一定要使用var命令,否则实际上声明的是一个全局变量。
三、如何从函数外部读取局部变量
以上例子证明了2点,作用域决定了这些变量的可访问性(可见性)。函数内部定义的变量从函数外部是不可访问的(不可见的)。但是往往出于某些原因,我们有时候需要得到局部变量。那么如何实现呢,请继续往下看。
其实很简单,我们可以在函数的内部,再定义一个函数。
function f1(){
var age=18;
function f2(){
alert(age);// 18
}
}
以上例子中,可以看见函数f2是定义在函数f1内部的,这也就代表在函数f2中可以访问函数f1内的所有局部变量。但是反过来在函数f1中是无法访问到函数f2中的局部变量的。这种结构就是javascript语言特有的“链式作用域”,子对象会一级一级地向上寻找所有父对象的变量。也就是,父对象的所有变量,对子对象都是可见的,反之则不成立。
而由于函数f2能访问到函数f1中的局部变量,那么只需要将函数f2作为返回值,我们就可以在函数f1外部读取到它的局部变量了。
function f1(){
var age=18;
function f2(){// 此函数f2就是闭包
alert(age);// 18
}
return f2;
}
var result = f1();
result();// 18
四、通俗易懂的闭包概念
以上代码中的f2函数就是闭包。
由此可见,闭包就是能够读取其他函数局部变量的函数,是联系函数内部和外部的一座桥梁。
又由于在javascript中,只有函数内部的子函数才能读取函数的局部变量,所以可以把闭包理解成”定义在一个函数内部的子函数“。
五、闭包的用途
前面我们已经了解了什么是闭包,现在我们来说说闭包具体的用途。
闭包的最大用途有两个,一个是之前提到的可以读取函数内部的局部变量,另一个是可以让这些变量的值始终保持在内存中。
如果不理解,可以看如下代码。
function f1(){// 此为函数f1
var age=18;// 此为局部变量age
ageAdd=function(){age+=1}// 此为全局变量,因为没有var关键字
function f2(){// 此为闭包函数f2
alert(age);
}
return f2;
}
var result = f1();
result(); // 18
ageAdd();
result(); // 19
分析以上代码,不难看出result实际上就是闭包函数f2。它一共运行了两次,第一次是直接获取函数f1的局部变量age,其值为18;第二次是先执行了ageAdd,对函数f1的局部变量age做了数据更改,然后再获取值,其值为19。由此可见,在第一次运行结束后,函数f1的局部变量age并没有被自动清除,而是一直保存在内存中,第二次运行时,再对其进行操作并读取出来。
那为什么局部变量age没有被清除,反而一直存在内存中呢?原因在于f1是f2的父函数,而f2被赋值给了一个全局变量result,这就导致f2始终在内存中,而又因为f2的存在依赖于f1,所以f1也始终在内存中,并不会在调用结束后,被垃圾回收机制回收。
注意:代码中的ageAdd在声明的时候由于并没有使用关键字var,所以ageAdd是一个全局变量。ageAdd的值是一个匿名函数,而这个匿名函数本身也是一个闭包,所以ageAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
六、使用闭包时的注意事项
通过以上分析可知:
1、闭包会使得函数中的局部变量都被保存在内存中。所以不难看出,闭包的使用会导致内存消耗大,所以我们不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决办法就是在退出函数之前,将不使用的局部变量全部删除。
2、闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当做对象(Object)使用,把闭包当做它的公用方法(Public Method),把内部变量当做它的私有属性(Private Value),在使用时就不要随便改变父函数内部变量的值。
以上就是有关闭包的详细解释。