JavaScript闭包
(个人学习总结,如有错误之处请指正。谢谢!)
作用域与作用域链
在了解闭包之前应深入了解作用域链的概念,了解了如何创建作用域链对理解闭包会有很大的帮助。
作用域就是变量和函数的可访问范围,包括全局作用域和局部作用域。而作用域链就是保证对作用域有权访问的所有变量和函数的有序访问。
1. 全局作用域:全局作用域指的是最外层的一个作用域。在Web浏览器中,全局作用域被认为是window对象,因此全局变量和函数都是作为window对象的属性和方法创建的。每个作用域中代码全部执行完毕后,该作用于环境就会被销毁,相对的其中的变量和函数定义也会随之销毁。(对于全局变量和函数,当关闭网页或浏览器时,才会被销毁。)
2. 局部作用域:某个函数的局部环境,当执行流进入一个函数时,就会将该函数的环境推入一个环境栈中。
var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
//这里可以访问tempColor, anotherColor和color
}
//这里可以访问color和anotherColor,但不能访问tempColor
swapColors();
}
//这里只能访问color
changeColor();
以上代码摘自JavaScript高级程序设计。
该代码段共涉及到三个作用域(执行环境):全局环境、changeColor()的局部环境、swapColors()的局部环境。构成了从父环境到子环境之间的关系:全局环境——>changeColor()的局部环境——>swapColors()的局部环境,若要访问一个变量,首先在该环境中查找该变量,若没有则向其上一级执行环境中查找,逐级以此模式向上查找直到全局环境未找到该变量,则是未定义的。即子环境可访问其父环境中的变量,但父环境不能访问子环境中的变量。
但是在实际应用中,我们会在某个执行环境外部访问其内部变量,这该怎么实现呢???
闭包
前面讲到了说要在一个函数的执行环境外部访问其内部变量,现在看看下面的代码:
function fun1(){
var a = "demo";
function fun2(){
console.log(a);//demo
}
}
在函数fun1()内部定义一个函数fun2(),可以在fun2()中访问fun1中的所有局部变量,既然这样,我们只需将fun2()作为fun1()的返回值,便可实现在外部访问fun1()的局部变量了。
function fun1(){
var a = "demo";
function fun2(){
console.log(a);//demo
}
return fun2;
}
var result = fun1();
result();
通过上述的代码我们可以看到,在fun1()外部通过result读取到了其内部变量。
其中fun2()函数就是一个闭包
我们很清楚的看到,闭包实际上就是一个函数,一个能够读取函数内部局部变量的函数。从本质上讲,闭包是连接函数内部与外部的桥梁,通过使用闭包,我们可以实现在函数执行环境外部访问到其局部变量。
在这里我们需要清楚一个概念,即就是function fun(){...}只是函数的定义,函数真正执行的时候是在调用函数的时候,当执行 fun();语句fun()函数才会执行。
闭包的用途
- 读取函数内部的变量
- 将某些值永久保存到内存中(所以要以正确合理的方式使用闭包)
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函数,通过两次运行结果的现实,我们可以知道局部变量n一直在内存中,并未因为函数执行完毕而被自动清除。
原因在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
使用闭包的注意点
- 闭包会使函数局部变量保存在内存中,内存消耗很大,因此不能滥用闭包。
- 闭包会在父函数外部,改变父函数内部变量的值。
——以上是自己当前对闭包的理解,若有不当之处,烦请翻阅之人指出,让我能在技术之路上慢慢成长。谢谢!