当浏览器加载了js代码之后,发生什么?js引擎怎么工作?编译器做了什么?

  以及执行环境对象、作用域链、活动对象、变量对象 是什么?

  以及作用域链什么时候创建、销毁?等等,这些对象的生命周期都会在js代码执行过程得到一一的体现

 先粗浅地了解下流程:

<script src='app.js'></script>
<script>
    var name = "Tom";

    function getInfo(){
        return name;
    }

    getInfo();
</script>
<script>
    var sex = 'male';
</script>

  1. 浏览器载入第一个代码段后,开始进行语法检测(书写是否正确等);之后,如果还有代码段,继续载入,语法检查;直到没有了代码段;

  2. js引擎 创建全局执行环境,同时生成作用域链、变量对象;将全局执行对象栈入环境栈中;(在浏览器中全局执行对象为window;全局执行环境在退出程序时销毁;)

  3. 编译器开始对全局执行环境中的var/function声明进行预解析;如果全局执行环境的变量对象中没有name、getInfo等,就添加它们

  4. 编译器预解析后,生成代码;js引擎开始执行代码;

以上几个步骤是非常粗浅的,省却了很多细节,但是js代码从加载到浏览器到执行完,基本就是这个过程;

下面结合代码、图表、解释等重新、详细地讲解下js代码的执行过程,这里举两个例子,一个是基本简单的流程,一个则涉及到闭包;

一个基本简单的例子:

var name = "Tom";

function getInfo(){

    var nation = "China";

    return name + nation;

}

getInfo();

  1. 编译器进行语法检测

  2. js引擎创建全局执行环境对象window,此时生成作用域链scope1,变量对象

  js代码执行过程-风君雪科技博客

  3. 编译器 对 var/function 声明的变量、函数预处理;将声明的变量name添加到变量对象中,值为undefined;

   function声明的函数变量添加到变量对象中,同时将作用域链scope1保存在[[scope]]属性中;

     同时,将全局执行环境栈入环境栈中;

  js代码执行过程-风君雪科技博客

    注1:函数的作用域链在函数声明时就已经创建,保存在内部属性[[scope]]中;在函数调用的时候会再次创建作用域链,用于延伸;

    注2:此时编译器没有对getInfo函数体做任何操作,

    注3:对var/function声明的变量预处理时,涉及到声明提前,函数声明优先的规则

    这一步处理完之后的代码相当于:

 1 function getInfo(){
 2     // 编译器此时没有对函数体中操作;
 3     // 只有函数被调用时,编译器才会处理函数体
 4     var nation = "China";
 5 
 6     return name + nation;
 7 
 8 }
 9 
10 var name;
11 
12 name = "Tom";
13 
14 getInfo();    

  4. 编译器生成代码,js引擎开始执行代码

    4.1 代码执行到12行时,js引擎 找到环境栈中顶部执行对象, 然后根据作用域链查找变量对象,发现变量对象中存在name,则赋值name=”Tom”

    4.2 代码执行到14行时,js引擎调用getInfo函数

      4.2.1 创建getInfo执行环境,同时生成作用域链,活动对象;

          此时生成的作用域链会将函数[[scope]]保存的scope1复制过来,再添加上新生成的getInfo活动对象;

          活动对象在生成时,会生成arguments对象;

          注:变量对象、活动对象区别

                                    变量对象:在生成全局执行环境时生成,不包含arguments

          活动对象:在调用函数,生成函数执行环境时生成,包含arguments对象

                                    讲述到现在一直没有出现作用域的概念,本人认为变量对象、活动对象就可以看成作用域;

      4.2.2 将getInfo执行环境对象栈入环境栈中,编译器开始对 var/function声明的变量预解析

         js代码执行过程-风君雪科技博客

        预解析后,相当于下面的代码:

 1 function getInfo(){
 2    
 3     var nation;
 4 
 5     nation = "China";
 6 
 7     return name + nation;
 8 
 9 }
10 
11 var name;
12 
13 name = "Tom";
14 
15 getInfo();

      4.2.3 编译器生成代码,js引擎开始执行

        4.2.3.1 执行到第5行,通过作用域链找到nation,然后赋值;

                             4.2.3.2 执行到第7行, 通过作用域链找到name,然后返回值

                             4.2.3.3 函数执行完,销毁getInfo活动对象、作用域链,将getInfo执行环境栈出,销毁;控制权交给环境栈中最顶部的执行环境;

基本简单的流程就是这样,如果有闭包,即使没有调用闭包,闭包[[scope]]指向的作用域链涉及的变量对象、活动对象不会销毁;

一个闭包相关的流程:

var name = "Tom";

function getInfo(){

    var nation = "China";

    function sumIn(){
        return name + nation;
    }

    return sumIn();
}

getInfo();

一个相对复杂的流程:

var name="Hello World";
var obj = {
    name:'obj Object',

    getName:function(){

        console.log( name );

        console.log(this.name);

    }
}
obj.getName();

 明晰这个流程,必须对作用域链非常了解;作用域链只与函数相关,跟对象无关;