• 浅谈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未声明,要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”这个函数的作用域中。

    如有错误,欢迎指正。

  • 相关阅读:
    大数加法、乘法实现的简单版本
    hdu 4027 Can you answer these queries?
    zoj 1610 Count the Colors
    2018 徐州赛区网赛 G. Trace
    1495 中国好区间 尺取法
    LA 3938 动态最大连续区间 线段树
    51nod 1275 连续子段的差异
    caioj 1172 poj 2823 单调队列过渡题
    数据结构和算法题
    一个通用分页类
  • 原文地址:https://www.cnblogs.com/chenmeng0818/p/5683315.html
Copyright © 2020-2023  润新知