闭包

闭包

一、什么是闭包

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的常用方式就是在一个函数内部创建另一个函数。

首先来对比一下下面的代码,理解一下闭包

1
2
3
4
5
6
7
8
9
function outer(){
var temp=10;
function inner(){
console.log(a);
};
bar();
}

//上面的函数inner对temp的引用是词法作用域的查找规则,这些规则只是闭包的一部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function outer(){
var temp=10;
function inner(arg1){
temp--;
if(arg1<temp){
return 0;
}else{
return 1;
}
}
return inner;
}

var test=outer();
test(8);//0
test(8);//1

//上面的函数inner的词法作用域能够访问outer内部的作用域,然后将inner()函数本身当作一个值类型进行传递。在outer()执行后,其返回值赋值给test并调用test(),实际上只是通过不同的标识符引用调用了内部的函数inner()

一个普通函数outer(),通常会期待在outer()执行后,整个内部作用域都被销毁,因为引擎右垃圾回收器来释放不再使用的内存空间。outer()的内容不会再被使用,所以很自然的会考虑将其回收。

而闭包的神奇之处就是可以阻止这件事情的发生,实际上outer内部作用域依然被引用着,因此没有被回收。是谁在 引用呢,就是inner本身。inner拥有涵盖outer内部作用域的闭包,使得该作用域一直存活,inner的这个引用就是闭包。

由于闭包会携带包含它的函数的作用域,因此会比其它函数占用更多的内存。过度使用闭包可能会导致内存占用过多,我们建议只在绝对必要的时刻载考虑使用闭包

二、闭包与变量

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

var test=createFunction();
test[0]();//10
test[1]();//10

三、函数的类型

创建函数的几种方式

  1. 声明函数

    1
    function fn(){}
  2. 匿名函数表达式

    1
    2
    var fn1=function(){}
    getFunctionName(fn1).length;//这种方式创建的函数为匿名函数,没有函数name

    注意:在对象内定义函数如var o={ fn : function (){…} },也属于函数表达式

  3. 具名函数

    1
    var fn2=function XXX(){}

    注意:具名函数表达式的函数名只能在创建函数内部使用

  4. Function构造函数

    1
    2
    //可以给 Function 构造函数传一个函数字符串,返回包含这个字符串命令的函数,此种方法创建的是匿名函数。
    Function("alert(1)");
  5. 自执行函数

    1
    2
    //让匿名函数自执行
    (function(){alert(1);})();
  6. 其它方法

    1
    eval\setTimeout\setInterval

四、注意

  1. 全局函数中的this等于window
  2. 当函数被某个对象的方法调用时,this等于哪个对象
  3. 匿名函数的执行环境具有全局性,this通常指向window