闭包(本身的学习+理解~~水水的)


  闭包是一个比较抽象的概念,水有点深,很容易绕进去,为了方便本身总结,先把从书本《javascript权威指南》中学到的一些点放在博客上面。javascript

  闭包是不少语言都具有的特性,在js中,闭包主要涉及到js的几个其余的特性:做用域链,垃圾(内存)回收机制等等。java

  一:函数做用域

  在js中,函数做用域是说变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。这与C/C++等的块级做用域有所不一样,因此你们在js中不要认为一个对象也是一个做用域。数组

  还有很重要的一点就是声明提早,只要经过var 定义了变量,不论是否赋值,这个变量始终存在于这个函数体内部。闭包

例子1

1
2
3
4
5
6
7
8
var scope="global"; 
var a = 1;
function t(){
var scope="local"
console.log(scope); //local
}
t();

例子2

1
2
3
4
5
6
7
var scope="global"; 
function t(){
console.log(scope); //undefined
var scope="local"
console.log(scope); //local
}
t();

 


  2、做用域链
 函数

做用域链图中很明确的表示出:在变量解析过程当中首先查找局部的做用域,而后查找上层做用域。ui

 

在代码一的函数当中没有定义变量i,因而查找上层做用域(全局做用域),进而进行输出其值。可是在代码二的函数内定义了变量i(不管是在alter以后仍是以前定义变量,都认为在此做用域拥有变量i),因而再也不向上层的做用域进行查找,直接输出i。可是不幸的是此时的局部变量i并无赋值,因此输出的是undefined。spa

代码一3d

1
2
3
4
5
var i = 10; 
function a() {
alert(i);
};
a(); //10

 

代码二指针

1
2
3
4
5
6
var i=10; 
function a() {
alert(i);
var i = 2;
};
a(); //undefined

 

3、内存回收机制

了解了做用域链,咱们再来看看js的内存回收机制,通常来讲,一个函数在执行开始的时候,会给其中定义的变量划份内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了,对应的内存空间也就被回收了。code

下次再执行此函数的时候,全部的变量又回到最初的状态,从新赋值使用。可是若是这个函数内部又嵌套了另外一个函数,而这个函数是有可能在外部被调用到的.而且这个内部函数又使用了外部函数的某些变量的话.这种内存回收机制就会出现问题.若是在外部函数返回后,又直接调用了内部函数,那么内部函数就没法读取到他所须要的外部函数中变量的值了。因此js解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一块儿保存起来,也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用之后(例如被删除了,或者没有了指针),才会销毁这个闭包,而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收。

 

4、闭包

个人理解是,闭包就是可以读取其余函数内部变量的函数。

因为在Javascript语言中,只有函数内部的子函数才能读取局部变量,所以能够把闭包简单理解成“定义在一个函数内部的函数”。

因此,在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁


例子1

1
2
3
4
5
6
7
8
9
10
11
12
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被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另外一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,所以 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个
匿名函数自己也是一个闭包,因此nAdd至关因而一个setter,能够在函数外部对函数内部的局部变量进行操做。

 

接下来的两个例子是告诉咱们一种避免使用闭包的方法。例子2是一个闭包,因为闭包使js的GC不会回收变量i,由于变量result的执行依赖于函数foo中的局部变量i。因为函数foo执行的时候,只是定义了result这个数组函数,因此最后的输出结果是三、三、3。

对比例子3,因为result这个数组是一个没有自执行的有传参的匿名函数,储存的是有传参的匿名函数(传入了当前的i),因此输出的结果是0、一、2。

例子2

1
2
3
4
5
6
7
8
9
10
11
12
13
var result=[];
function foo(){
var i= 0;
for (;i<3;i=i+1){
result[i]=function(){
alert(i)
}
}
};
foo();
result[0](); // 3
result[1](); // 3
result[2](); // 3

result[0] = result[1] = result[2] = function(){  alert(i)  }

例子3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var result=[];
function foo(){
var i= 0;
for (;i<3;i=i+1){
result[i]=(function(j){
return function(){
alert(j);
};
})(i);
}
};
foo();
result[0](); // 0
result[1](); // 1
result[2](); // 2

result[0] = result[1] = result[2] = function(){  alert(j)  }

相关文章
相关标签/搜索