Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

js运行三部曲 #2

Open
Qhappyman opened this issue Feb 28, 2019 · 0 comments
Open

js运行三部曲 #2

Qhappyman opened this issue Feb 28, 2019 · 0 comments

Comments

@Qhappyman
Copy link
Owner

js运行三部曲

  • 语法分析

    js代码运行是一条一条的解释,而在解释前要进行的工作 就是语法分析,对全部语句进行一步检查,判断是否符合基本语法规则。

  • 预编译

    预编译前奏:

    1. 一切未经声明的变量都是全局变量,属于全局作用域window所有

      a = 1;
      

      不会报错,在全局范围内相当于

      var a = 1;
      

      a属于全局window,在全局的作用域内,但是不提倡这样。

      再来看一段

      function my(){
          var a = b = 3;
      }
      

      变量的赋值从右到左,相当于西安将3赋值给了b,然后a的值等于b。但是此时只有a被声明,b并未声明,所以b属于全局变量,作用域在window,而a则是局部变量,属于函数my;

    2. 变量 声明提升;函数声明整体提升。看例子

      var a = 1;
      console.log(a);
      

      此时打印出结果为 1 ;

      console.log(a);
      var a = 1;
      

      将a定义到输出下面,但是并不会报错。此时a的定义可分为倆步。

      var a; //声明 a;
      a = 1; //给a赋初值;
      

      当a被声明时,同时被提升到了作用域的顶端,所以相当于

      var a;
      console.log(a);
      a = 1;
      

      因此,打印出的是undefined;再来看

      function my(){
          var b = 1;
          console.log(b);
      }
      my();
      

      会打印出 1,没有问题

      my();
      function my(){
          var b = 1;
          console.log(b);
      }
      

      同样会打印出1.在js中,当函数被声明时他就被提到了作用域的顶端,而且是整个函数都被提升了,因此,my()函数执行无误;

      预编译四部曲

      1. 创建AO对象

      2. 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined

      3. 将实参值和形参统一

      4. 在函数体里找函数声明。值赋予函数体

      在js代码执行前会创建一个GO对象,也就是全局作用域,而在每个函数执行前一刻会创建自己的AO对象,也称执行期上下文,看下面一段代码

      function my(a){
          console.log(a);
          var a = 123;
          console.log(a);
          function a(){}
          console.log(a);
          var b = function(){}
          console.log(b);
          function d(){};
      }
      my(1);
      

      在这个函数执行前,首先创建一个自己的AO对象(执行期上下文)

      AO{
          
      }
      

      第二步

      AO{
          a:undefined;
          b:undefined;
      }
      

      形参为a,变量声明为a和b,全部赋值undefined

      第三步

      AO{
          a:1;
          b:undefined;
      }
      

      a的实参为1.b无实参

      第四步

      AO{
          a:function a (){};
          b:undefined;
          d:function d (){}
      }
      

      找到函数声明并赋予,至于function b仅仅是一个函数表达式,并不是函数声明,至此,AO对象创建完毕,开始执行函数。这里有几个注意点

      • 声明的函数和变量在函数执行时忽略,因为都提升到了顶端

      • 对于 var a = 123; var b = function(){};这样的声明,虽然变量声明被提升了,但是前面讲过,这些语句可分为俩段。

        var a;
        a = 123;
        var b;
        b = function(){};
        

        因此,var a 与var b被提升忽略不看,但是俩条赋值语句仍在原地,当解释执行到此时会理所当然地将值赋予给变量。

        所以最后的打印结果应该是

        function a(){}
        123;
        123;
        function b(){};
        

        总的来说,变量声明提升和函数提升势必要覆盖其中一个,有一定的执行顺序,结果就是函数覆盖了变量。根据上面4点从后往前的访问顺序执行函数。

        GO对象

        GO即全局作用域,既然AO是在函数执行前一刻创建的,那么GO则是在window加载前一刻创建的全局执行期上下文,创建规则与AO相同。而对于函数

  • 解释执行

    当上述预编译执行完毕后,形成了GO和AO对象,js代码就可逐条执行,叫做解释执行,相关的作用域链在下一次介绍。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant