一、全局变量

1、在页面脚本中直接用var关键字声明的变量称为全局变量,全局变量会作用于整个脚本,比如

<script>

     var a=10;//  a就是全局变量;而这个脚本就是它的作用域

</script>

2、还有一种情况,在函数内部不用var关键字声明的变量同样属于全局变量,比如

<script>
    function cc(){
    a=5;//这里的a不用var关键字声明,但也是全局变量。
}
</script>

二、局部变量

函数内部用var关键字声明的变量称为局部变量,局部变量的作用范围仅限于函数内部。比如

<script>
     function dd(){
             var a=3;// 这里的a就是局部变量,作用域仅限于当前函数内部。
    }
</script>

该进入正题了。。。

三、闭包

当函数内部有对象能够保留函数内部数据时,我们称这个对象就是一个闭包。比如

function outer() {
  var a = 3;

  function inner() {
    alert(a);
    a++;
  }

  return inner;  //返回inner,好给外部变量引用;
}

var result = outer();//这个时候result就能让inner中的a值保留在内存中,产生了闭包,这个闭包就是inner,
result();//3
result();//4
result();//5

a的值会保留在inner中,这就是闭包的神奇而又强大的地方。

很可能很多人这个时候还是不明白什么是闭包,为什么会出现连加的情况,这就要讲一下作用域链的原理了。请看一下这个函数。

var  n=3;
function Test(){
    alert(n);
   var n=4;
}
Test();

大家猜猜,运行的结果会是什么,很多人一下子就会说结果为:3;错!这时就又改变主意了,结果为:4;同样是错!正确的结果是:undefined,为什么会是这样呢,这就是函数作用域链的问题了,如果函数内部没有再次声明n的话,结果就是3,可惜函数内部声明了n,局部变量优先权高于全局变量,所以局部变量n会先放在作用域链的顶端,alert(a)要访问n值得从这个顶端进去查找,虽然作用域链中保存着n的指针,但还没给n赋值,所以会返回undefined。

理解了这个作用域链,我们就很容易理解闭包了。

又说回我们最开始的那个函数了,为了方便浏览,再次把它写在下面。

function outer() {

  var a = 3;

  function inner() {

    alert(a);

    a++;

  }

  return inner;  //返回inner,好给外部变量引用;

}

var result = outer();//这个时候result就能让inner中的a值保留在内存中,产生了闭包,这个闭包就是inner这个内部函数,它保留着a的值。

result();//3

result();//4

result()//5

其中inner就是闭包,也就是因为产生了闭包,它把每次调用result()后的a保留下来了,既然闭包(inner)中已经存在a,那为什么还要到父函数outer那里获取a值呢,现在已经没这个必要,因为inner中的a被保留下来,顺着作用域链,首先读取的是inner中保留着的a,而不会再去读取外层a 了,所以继续调用result的话a值会继续增加。

这个时候不知道我讲清楚没有,如果还不清楚的话请继续看下面一个函数,它只是outer函数的一个变形。

function outer(){
 var a=3;
 nAdd=function inner(){
         alert(a);
         a++;
  } 
}
outer();调用了outer函数,但产生了一个闭包,这个闭包同样是inner
nAdd();//3
nAdd();//4
nAdd();//5

那为什么outer没有寄存在外部变量中还可以产生闭包呢?不,outer虽然没有依赖外部变量,但在第一次的调用中却产生了一个全局变量,那就是nAdd,虽然outer没有寄存在nAdd上,但inner寄存了,由于nAdd是全局变量,它不会被浏览器的垃圾机制回收,所以inner中的数据也跟着保留下来。

最后,本文目的只为了让闭包更通俗易懂,描述的不专业也不严谨,就当做抛砖引玉吧。