简明闭包

字数 986阅读 5

闭包向来是javascript的难点之一。

1.定义
2.闭包是如何产生的
3.不仅仅是词法作用域

一 定义

闭包可以看作是javascript函数的一种特性。

二 闭包是如何产生的

从程序语言的角度来看,闭包产生的原因,就是基于词法作用域的作用域链的访问规则。

javascript中作用域只有两种类型,天生的全局环境和函数调用生成的局部环境。

嵌套的调用会产生多个嵌套的环境,就像栈那样。

1.png

对于语言的设计者来讲,就有了一个必须解决的问题,那就是:当你在局部作用域里找不到某一个标识符的时候,是否跨作用域寻找。
最简单粗暴的策略是,找不到就报错,不容许跨作用域找查。

/* 1.0 */
var a=10;
function fun_1(){
        console.log(a);
}
fun_1();            //如果语言被设计为不容许跨越作用域 那么这段代码报错

这种设计比较简(愚)单(蠢),函数无法获得外部的任何信息,如果必要,信息要以参数的形式传入。

第二种策略,容许标识符的解析跨越作用域,也就是说,这个作用域里找不到这个标识符,那么就到上一个作用域中去找(当然不能去下一个,因为下一个作用域还没有生成呢)。
几乎所有的编程语言都支持这种策略,但是在这种策略的具体实现上,设计者们产生了分歧,一个新的问题是: 到底谁才是上一个作用域?。
由于函数调用就像栈的压,出,理所当然的一种策略就是,按调用的顺序来,每一个调用者的作用域都是被
调用者的上一个作用域,就也就是动态作用域。

/* 1.1 */
var a=10;
function fun_1(){
        console.log(a);
}
fun_1(); // 输出10
(function(){
var a='xxx';
fun_1();//输出 ‘xxx’
})();

这种动态作用域,很依赖于函数调用的位置,不同的位置,函数访问的上一个作用域也不相同。
javascript的设计者明显不喜欢这样的设计,语言的设计者,希望函数的上一个作用域,在声明或者是定义的那个地方就确定了,就像一个隐藏的bind。
正所谓,javascript函数运行在它们被定义的作用域内,而不是它们被执行的作用域内,这就是所谓的词法作用域,与函数在那里调用无关。

/* 1.2 */
var a='galbol';
function fun_1(){
          console.log(a);
} //全局环境下定义
fun_1(); // 输出'global'    //全局环境下调用
(function(){
var a='xxx';
fun_1();//输出 ‘global’  //嵌套在函数中调用
})();

这个例子可以看到,我们在全局作用域中定义了函数fun_1,所以,fun_1的上一个作用域即全局作用域,不管在那里被调用,都不会被影响。
这样就产生了闭包,当然上述的例子很单调,无法展现闭包的强大之处,看一下这个例子。

/* 1.3 */
function fun_1(){
  var x=100;
return function(){
  console.log(x);
  }
}
fun_2=fun_1();
fun_2() //输出100

fun_1中定义了一个匿名函数,根据词法作用域,匿名函数的上一个作用域,也就是fun_1调用时生成的局部作用域,即时fun_1调用完毕,这个作用域也不能被垃圾回收处理,因为返回的这个匿名函数,可能使用了fun_1局部作用域中的数据,也只有这个匿名函数能够访问到这个局部作用域中的数据,这就是闭包的强大之处。
<h3>3.不仅仅是词法作用域</h3>
在javascript中,有两个特殊的例子
this this在javascript是个关键字,它的值取决于调用位置,即动态的。
Function构造函数生成的函数,也是闭包,不过函数的上一个作用域永远是全局作用域,而不是声明和定义它们的地方。

推荐阅读更多精彩内容