提及javascript中的闭包,首先要知道为何会存在闭包,其做用又是什么。且为何闭包中就能让外层函数的变量始终保存呢?下面咱们将从这两个角度去剖析它。固然,大神绕道,谢谢哈。javascript
开门见山,直接总结闭包的两大核心做用:html
众所周知,变量在javascript中有全局变量和局部变量,函数内部能够访问外部的全局变量,而外部没法访问函数内部的局部变量,这是JS的一个特色:java
var n = 999;
function f1() {
console.log(n)
}
f1(); // 999
复制代码
那么摆在咱们面前的一个问题就是:如何在函数外部访问到函数内部的变量?有一种办法就是在函数内部再定义一个函数,经过这个内层函数去访问外层函数的变量,由于内层函数同属外层函数的做用域中(符合链式做用域结构规则,子对象能够一级一级地向上寻找全部父对象的变量):面试
funtion f1() {
var n = 999;
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); // 999
复制代码
对此:阮一峰老师给出了一个通俗的关于闭包的解释: 闭包就是可以读取其余函数内部变量的函数(关于究竟是内层函数是闭包仍是外层函数是闭包的解释各方不一致,但这不是重点)。数组
因为在JS中只有函数内部的子函数才能读取局部变量,所以也能够理解为定义在一个函数内部的函数。bash
暂且咱们说闭包是指有权访问另外一个函数做用域中的变量的函数吧,函数内部的函数使用到外层函数的变量,使得外层函数的变量的生存时间延长,形成常驻内存。闭包
function foo(){
var a = 2;
return function(){
a += 1;
console.log(a);
}
}
var baz = foo();
baz(); // 3
baz(); // 4
baz(); // 5
baz(); // 6
复制代码
上面的例子为何每次调用baz时,a都没有被初始化赋值呢? 接下来就要从JS的垃圾回收机制去考虑了。函数
一般咱们可使用引用计数法判断代码中的变量是否被释放,即语言引擎中有一张“引用表”,保存了内存里面全部的资源(一般是各类值)的引用次数,若是一个值的引用次数为0,则表示该值再也不用到了,所以改值也被内存释放了。好比:学习
cont arr = [1,2,3];
// 数组[1,2,3]是一个值,会占用内存变量arr是仅有的对这个数组的引用,所以引用次数为1。尽管只赋值一次,后面代码中再没有调用,但却依然占据内存.
复制代码
譬如JS这样的高级程序语言中都嵌入了一种称为垃圾回收器的机制,其工做是跟踪内存的分配和使用,以便发现任什么时候候再也不须要的内存并对其进行释放。举例以下:ui
var o1 = {
o2: {
x: 1
}
};
//建立2个对象o2和o1,其中o2被o1对象引用做为其属性,此时没有垃圾可收集
var o3 = o1; //建立变量o3,引用由o1指向的对象的变量
o1 = 1 ; //如今将o1从新赋值为1,最初的o1中的对象由o3变量表示
var o4 = o3.o2; //建立变量o4,引用对象o2,此时o2被两个地方引用:一个是做为o3变量的属性,一个是做为o4变量
o3 = '666'; // 此时最初o1对象应没有再被引用了,能够被垃圾收集了,可是最初的o2还在被o4引用,所以还不能被垃圾收集
o4 = 16; //此时 o2也能够说再见了...
复制代码
好了,如今再回过头来看看上面那个闭包题,用垃圾回收的思想来做出解答:
function foo(){
var a = 2;
function outer() {
a += 1;
console.log(a);
}
return outer
}
var baz = foo(); //在全局做用域下建立变量baz
复制代码
此时,baz指向的就是 outer,因此outer始终都没有被销毁,而根据垃圾回收机制,因为在outer中有引用外层函数的变量a,所以a也一直没有被销毁,因此就出现这种现象:
baz(); //3
baz(); //4
baz(); //5
复制代码
是否是如下就明白了,接下来看一个简单的经典例题
看一下经典的面试题吧,用垃圾回收的思想来思想一下结果,就会很顺利:
var callbacks;
for(var i = 0 ; i <= 5 ;i ++) {
callbacks = function() {
console.log(i);
}
}
callbacks(); //6
callbacks(); //6
callbacks(); //6
复制代码