• 浅谈javascript中的作用域


    所谓的作用域,可以简单理解为一个可以读、写的范围(区域),有些js经验的同学可能会说:"js没有块级作用域",js除了全局作用域外,只有函数可以创建作用域。作用域的一个好处就是可以隔离变量。

    我们通过一些例子来帮助我们理解js中的作用域。

    1 alert(a);
    2 var a = 1;

    如果对作用域一点不了解的同学可能会说 alert的是1或者报错;但实际上是undefined;

    说到这里,我们首先说一下js逐行解析代码之前做的一些准备工作,

    js在逐行读代码之前,会做一些“预解析”工作,会先提前找到一些”小东西”,当然”js解析器“不会随便找一些数据的,它会根据var,function,参数来找。

    ”js解析器“它比较”懒“,在正式运行代码之前都会给var声明的变量赋值为undefined,也就是var a = undefined;会把整个函数看作一个代码块,不去管里边有多少代码。参数等到后边例子中会说。

    当所有准备工作都做好后,“JS解析器”就开始逐行执行代码了,现在我们来分析开始的这个例子就很容易明白为什么是undefined了。

    再来看下边这个例子

    1 alert(a);
    2 var a = 1;
    3 alert(a);
    4 var a = 2;
    5 alert(a);

    我们来一点点分析这个

    首先 ”预解析“: 解析器会找var

    读到第二行时  a = undefined;

    读到第四行时 依然  a = undefined;

    正式逐行执行代码:

    第一行 alert:undefined 

    第二行  a = 1;

    第三行 alert:1;

    第五行 alert:2

    接着看下边这个例子

    1 alert(a);                    
    2 var a = 1;
    3 alert(a);                    
    4 function a (){ alert(2); }
    5 alert(a);                    
    6 var a = 3;        
    7 alert(a);                    
    8 function a (){ alert(4); }
    9 alert(a);    

    我们依然来一点点分析这个

    首先 ”预解析“: 解析器会找var function;

    读到第二行时 a = undefined;

    读到第四行时 a = function a (){ alert(2);} //所有的函数,在正式运行代码之前,都是整个函数块;变量遇到重名的,只留一个变量,如果变量和函数重名,就只留下函数。

    读到第六行时,a = function a (){ alert(2);}

    读到第八行时,a = function a (){ alert(4);}

    正式逐行执行代码:

    第一行 alert: function a (){ alert(4);} 

    第二行  a = 1; //表达式可以修改预解析的值!

    第三行 alert:1;

    第四行 函数没有调用,略过;

    第五行 alert:1;

    第六行 a = 3;

    第七行 alert:3

    第八行 函数没有调用,略过;

    第九行  alert:3

    如图所示:

     

    继续看例子:

    1 var a = 1;
    2 function fn1(){
    3     alert(a);      //undefined                   
    4     var a = 2;
    5 }
    6 fn1();
    7 alert(a);   //1

    首先 ”预解析“: 解析器会找var function

    读到第一行时 a = undefined;

    读到第二行时 fn1 = function fn1 (){alert(2);var a = 2;}                             

    正式逐行执行代码: 第一行 a = 1;

    第六行 函数调用,进入函数作用域 在函数作用域内依旧是先预解析,再逐行执行

      函数内预解析:a = undefined;

      执行:alert:undefined;

      a = 2;  //此时的a仅为函数作用域中的a,不会影响全局中的a

    函数执行完毕,回到全局作用域;

    第七行 alert:1;

    继续:

    1 var a = 1;
    2 function fn1(){
    3     alert(a);       //1             
    4     a = 2;
    5 }
    6 fn1();
    7 alert(a);    //2

    这个例子上边那个例子唯一的区别就是函数中的a没有var,只分析其中关键的地方

    在函数作用域中 第三行alert(a),由于函数中没有var a,所以"解析器"会到函数的作用域的上一级作用域去寻找a(作用域上下级关系的确定就看函数是在哪个作用域下创建的,在哪个作用域下创建,就是哪个作用域的下级),此时函数的上一级是全局作用域,在全局作用域中,a = 1,所以此时第三行 alert:1,接着第四行,a = 2赋值,依然是函数作用域中没有a, 所以在上一级作用域,也就是全局作用域中找到a,修改全局作用域中的a, 所以会使全局作用域中的a = 2, 因此第七行 alert:2;

    这点要理解清楚,注意有无var的区别。

    接着来:

    1 var a = 1;
    2 function fn1(a){
    3     alert(a);     //undefined                
    4     a = 2;
    5 }
    6 fn1();
    7 alert(a);   // 1

    这个例子和上一个的区别就是多了个参数,参数的作用相当于局部变量,也就是在函数中预解析会有var a = undefined,所以第三行 alert:undefined,第四行 a = 2 改的是函数作用域中的a,不影响全局中的a,第七行alert:1;

    接着:

    1 var a = 1;
    2 function fn1(a){
    3     alert(a);                        // 1
    4     a = 2;
    5 }
    6 fn1(a);
    7 alert(a);          // 1

    这个例子又与上一个有些区别,在第六行函数调用时传了个实参进去,第六行函数实参的a是全局变量a = 1的1,函数执行时,第二行 a = 1,所以第三行alert:1,第七行alert:1。

    注意这几个例子之间的区别,别混淆了。

    再来一个:

    1 var a = 1;
    2 function en(){
    3     var a = 2;
    4     fn();
    5 }
    6 function fn(){
    7     alert(a);           //1
    8 }
    9 en();

    fn中的a未声明,要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”这个函数的作用域中。

    如有错误,欢迎指正。

  • 相关阅读:
    文件合并
    排序
    canvas 的cliprect()实现画布剪切DEMO
    SurfaceViewDemo
    View实现事件监听DEMO(文本跟随触屏事件)
    android progressBar和seekBar的小DEMO
    Android DrawerLayoutDemo
    Fragment和FragmentActivity使用Demo
    SharedPreferences DEMO
    android中sharedPreferences的用法
  • 原文地址:https://www.cnblogs.com/chenmeng0818/p/5683315.html
Copyright © 2020-2023  润新知