js代码执行过程
当浏览器加载了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,变量对象
3. 编译器 对 var/function 声明的变量、函数预处理;将声明的变量name添加到变量对象中,值为undefined;
function声明的函数变量添加到变量对象中,同时将作用域链scope1保存在[[scope]]属性中;
同时,将全局执行环境栈入环境栈中;
注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声明的变量预解析
预解析后,相当于下面的代码:
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();
明晰这个流程,必须对作用域链非常了解;作用域链只与函数相关,跟对象无关;