js运行三个阶段

  1. 语法分析
  2. 预编译
  3. 解释执行

语法分析就是JS引擎去检查你的代码是否有语法错误,解释执行就是执行你的代码。最重要最需要理解的就是第二个环节预编译,简单理解就是在内存中开辟一些空间,存放一些变量与函数 。

预编译可分为全局预编译和局部预编译。

  1. 在js脚本加载之后,会先通篇检查是否存在低级错误;
  2. 在语法检测完之后,便进行全局预编译;
  3. 在全局预编译之后,就解释一行,执行一行;
  4. 当执行到函数调用那一行前一刻,会先进行函数预编译,再往下执行。

全局预编译的3个步骤

  1. 创建GO对象(Global Object)全局对象,即window对象。
  2. 找变量声明,将变量名作为GO属性名,值为undefined
  3. 查找函数声明,作为GO属性,值赋予函数体

    局部预编译的4个步骤

  4. 创建AO对象(Activation Object)执行期上下文。

  5. 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
  6. 将实参值和形参统一。
  7. 在函数体里面找函数声明,值赋予函数体。

    GO对象是全局预编译,所以它优先于AO对象所创建和执行

案例分析

  1. <script>
  2. var a = 10;
  3. console.log(a);
  4. function foo(a) {
  5. console.log(a);
  6. var a = 100;
  7. console.log(a);
  8. function a() {}
  9. console.log(a);
  10. var b = function(){};
  11. console.log(b);
  12. function d() {}
  13. }
  14. var c = function (){
  15. console.log("匿名函数C");
  16. };
  17. console.log(c);
  18. foo(20);
  19. </script>

全局预编译

  1. GO/window = {
  2. a: undefined,
  3. c: undefined
  4. foo: function(a) {
  5. console.log(a);
  6. var a = 123;
  7. console.log(a);
  8. function a() {}
  9. console.log(a);
  10. var b = function() {}
  11. console.log(b);
  12. function d() {}
  13. }
  14. }

解释执行代码

直到执行调用函数foo(20)语句

  1. GO/window = {
  2. a: 10,
  3. c: function (){
  4. console.log("I at C function");
  5. }
  6. test: function(a) {
  7. console.log(a);
  8. var a = 123;
  9. console.log(a);
  10. function a() {}
  11. console.log(a);
  12. var b = function() {}
  13. console.log(b);
  14. function d() {}
  15. }
  16. }

局部预编译

调用函数foo(20)前发生

  1. // 局部预编译前两步:
  2. AO = {
  3. a:undefined,
  4. b:undefined,
  5. }
  6. // 局部预编译第三步:
  7. AO = {
  8. a:20,
  9. b:undefined,
  10. }
  11. // 局部预编译第四步:
  12. AO = {
  13. a:function a() {},
  14. b:undefined
  15. d:function d() {}
  16. }

预编译总结:

  1. 函数声明整体提升-(具体点说,无论函数调用和声明的位置是前是后,系统总会把函数声明移到调用前面)
  2. 变量 声明提升-(具体点说,无论变量调用和声明的位置是前是后,系统总会把声明移到调用前,注意仅仅只是声明,所以值是undefined)