首页
变量作用域
变量作用域
版权声明:本文为原创内容,转载请声明出处。
原文地址:http://www.excelib.com/article/232/show

嵌套函数变量作用域

我们这里所说的变量作用域主要指使用var所定义的变量的作用域,这种变量的作用域是function级的,这一点我们在前面讲var语句的时候已经给大家介绍过了。在ES中的function是可以嵌套使用的,嵌套的function中的变量的作用域又是怎样的呢?我们先来看个例子

1
2
3
4
5
6
7
8
9
var v=0;
 function f1(){
     var v=1;
     function f2(){
         console.log(v);
     }
     f2();
 }
 f1();             // 1

这里定义了全局变量v,在函数f1中又定义了局部变量vf2定义在f1函数中,当调用函数f1时会在其内部调用函数f2f2中使用到了变量v,这时v会使用f1函数中定义的v。要想弄明白这个问题,大家需要理解函数作用域链。

作用域链

嵌套函数在调用时解析器会根据嵌套的层次自动创建一个函数作用域链,然后将各层次函数所定义的变量从外到内依次存放到作用域链中,比如上面的例子中会首先将全局对象(浏览器中指页面本身,也就是Window对象)放到最下层,然后放f1,最后放f2,我们可以在f2函数中加入断点然后使用FireBug清楚的看到这一点,截图如下

53046.png

1

作用域链的作用

现在大家明白了什么是函数作用域链,以及作用域链的层次关系,那么他有什么作用呢?下面学生就来给大家解释。

在一个函数中使用到变量的时候就会在函数所在的变量作用域链中从上往下查找变量的值,比如上面的例子中会先在f2中查找,如果找不到就会去f1查找,如果还查找不到就会到全局变量中查找。从图1中我们可以看到f2中没有v变量,所以会向下查找f1,在f1可以找到变量v,找到后就会使用f1中定义变量v,而不再继续向下查找了,所以最后会将“1”打印到控制台。

我们再来看一个稍微复杂点的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var x= 0, y= 0, z= 0, w=0;
 function f1(){
     var y=1, z=1, w=1;
     function f2(){
         var z= 2, w=2;
         function f3(){
             var w=3;
             console.log(x);
             console.log(y);
             console.log(z);
             console.log(w);
         }
         f3();
     }
     f2();
 }
 f1();

这个例子最终会输出什么呢?

这里每个变量查找的顺序都是f3>f2>f1>全局变量,对于x来说会一直到全局变量才能找到,y会在f1中找到,z会在f2中找到,w直接使用f3自己定义的局部变量,所以最后输出的结果是

1
2
3
4
0
1
2
3

 

多知道点

调用子函数与嵌套函数中变量查找的区别

在变量作用域中很容易混淆子函数和嵌套函数中变量的查找过程,嵌套函数中变量会按照函数嵌套的层次在作用域链中从上往下查找,而在调用子函数时并不会使用父函数中的变量,比如下面的例子

1
2
3
4
5
6
7
8
9
var v=0;
 function logV(){
     console.log(v);
 }
 function f(){
     var v=1;
     logV();
 }
 f();

这里在函数f中调用了子函数logVlogV中打印了变量v,这时的v会使用全局变量而不会使用f中的局部变量,所以最后会打印出0,这是因为在调用logV函数时又会创建新的作用域链,新建的作用域链只会包含logV函数嵌套定义的层级而不会包含调用时的f函数,也就是说这个例子中有两套独立的作用域链,调用f函数时有一套,在f中调用logV函数时又有另外一套,他们都是只有两层,第一层为全局变量,第二层为函数自身,所以logV函数中会使用全局变量v而不会使用f函数中的v(因为f函数根本不在logV函数调用的作用域链中)。

大家只需要记住这样一个原则就可以了:作用域链跟函数定义的位置有关,跟调用的位置无关