前言:
闭包是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未定义
首先来看一个实例:
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()是正确的 }
在来看一个实例:
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(); // 1000result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在分成有必要的时候才使用闭包。
小结:
闭包就总结到这里,这次运用了大量的例子来说明闭包,希望阅读的朋友能够看懂。