前言:
闭包是Javascript语言的一个难点,也是它的特色,很多高级的应用都要依靠闭包来实现。之前看李炎恢老师虽然讲的特别的好,当时理解的并不是很深刻,今天通过实例来更好的解释什么是闭包,希望看后的朋友能够理解。
本篇文章分三部分来讲解闭包,首先说变量的作用域,然后说内部函数,和如何从外部读取局部变量,最后引入我们今天的主题——闭包。这篇文章用到的例子比较多,希望认真阅读,最后敲例子更能充分理解
一、变量的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
在JavaScript中,变量的作用域分两种:全局变量和局部变量。
在Javascript中,在函数内部可以直接读取全局变量。
var n=123;//定义全局变量n
function f1(){
alert("在函数内部访问全局变量n,n="+n);//在函数内部访问全局变量n
}
运行结果:
但反过来不行,在函数的外部无法读取函数内的局部变量
function f1(){
var n=123;//在f1函数内部定义局部变量n
}
alert("在函数外部访问局部变量n,n="+n); //在函数外部访问局部变量n,错误:n未定义
需要注意的是,函数内部声明变量的时候,一定要使用var命令。如果不用的话,实际上是声明了一个全局变量!
二、内部函数
首先来看一个实例:
function A(){
function B(){ //B就是一个内部函数
alert("你好!");
}
}
由于函数B在函数A的内部,因此B函数在函数A内有效,但是在外部调用时无效的,如:
window.onload = B(); //外部调用无效
function A(){
alert("你好");
function B(){ //B就是一个内部函数
alert("你好!");
}
} //报错误 B is not defined
但是在内部调用是正确的
window.onload = A();
function A(){
function B(){
alert("你好!");
}
B(); //内部调用A()是正确的
}
javascript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,javascript垃圾收集器才能释放内部函数的内存空间。也就是说,只要存在调用内部函数的可能,javascript就要保留被引用的函数。
三、如何从外部读取局部变量
在来看一个实例:
function f1(){
var n=123;//f1函数内部的局部变量n
//在f1函数内部定义一个f2函数
function f2(){
//在f2函数内部是可以访问局部变量n的
alert(n); // 123
}
}
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1 就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,JavaScript中的函数名本身就是变量,所以函数也可以当作普通变量来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的返回值返回。
function f1(){
var n=123;//局部变量n
//在f1函数内部声明的f2函数
function f2(){
alert(n);
}
return f2;//将f2函数作为f1函数的返回值
}
var result=f1();//f1调用完后的返回值是一个f2函数,此时result就是f2函数
result(); // 123,调用f2函数
看了上面的实例我想应该对闭包的组成有了一定的了解,那么我们来说一下闭包
四、闭包的概念
在看视频中,闭包:是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量,由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
五、闭包的作用
(1)函数外部访问函数内部的函数或变量;
(2)常驻内存;
实例:
function f1(){
var n=999;
nAdd=function()
{
n += 1;
}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量(result),这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
六、注意
由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在分成有必要的时候才使用闭包。
小结:
闭包就总结到这里,这次运用了大量的例子来说明闭包,希望阅读的朋友能够看懂。