首页 > 代码库 > 深入理解Javascript的var变量和闭包

深入理解Javascript的var变量和闭包

今天无意间看到阮一峰校友写的一个关于ES6新特性let。let的出现,就是为了弥补var的不足。由此,写下这篇博客重新回顾一下Javascript老生常谈的闭包和它的一个作用域的诟病。


闭包的概念我就不说了,很多书上都又说明和解释,就是在一个函数内部可以访问函数外的上下文。今天重点来说以下var变量。因为平时开发的时候,如果不对这个关键字有一个很深刻的认识,很容易产生很多坑和错觉。

本文要阐明的观点是:由var定义的变量,它作用域在一个函数体内,而不是我们其他语言理解的大括号{ }内。

下面又几个经典的小例子:

(function(){
        var a = [];
        for (var i = 0; i < 10; i++) {
            var c = i;
            a[i] = function () {
                console.log(c);
            };
        }
        a[6](); // 9

    })()





这个例子稍微有点Javascript基础的人估计都知道,这也是《Javascript Good Parts》里头特别说明过的一个例子。该例子中输出的c是9而不是6。

解释:首先,在函数a[6]中,是可以访问到变量c的,而且,在执行a[6]函数前,此时c的值已经是9了。


没理解我说的?或者说我已经理解了上面那个例子了?好,没关系,再来看下面这个例子:

(function(){
        if(true){
            var a=10;
        }
        console.log(a); //输出10 而不是undefined
})();

在上面这个例子中,输出结果是什么?学过java或其他成熟的面向对象语言的人都可能会以为输出的是undefined。但是,让你们失望了,输出结果真的是10。

不理解?这个时候回顾一下我一开始说的:“由var定义的变量,它作用域在一个函数体内,而不是我们其他语言理解的大括号{ }内。”,是不是明朗了很多?


同样的,下面的这个例子中,输出的是10,而不是5:

function f1() {
  var n = 5;
  if (true) {
      var n = 10;
  }
  console.log(n); // 5
}

这也就是为什么ES6新特性中有个let关键字。let的作用域是块级的(而不是函数级的),跟我们传统理解的其他语言的作用域是一样的。


好了,理解了之后来做个小练习吧:


(function(){
        var n=1;
        var arr=[];
        var arr2=[];
        for(var i=0;i<10;i++){
            var c=i;
            arr[i]=function(){
                for(var j=0;j<4;j++){
                    c=c+10;
                    arr2[j]=function(){
                        console.log(c);
                    }
                }
                return c;
            }
        }
        //console.log(‘c:'+c); //c=9
        console.log(arr[6]());  //c=49
        //console.log('c:'+c); //c=49
        arr2[3]();//c=49
    })();

上面两个地方c的输出结果可以理解了吗?其实个人认为这是Javascript中的一个大坑。没有彻底明白的人很容易迷糊。


另外,可以参见阮一峰大神的 var && let:

http://es6.ruanyifeng.com/#docs/let


深入理解Javascript的var变量和闭包