js执行流程

163 阅读5分钟

js 执行流程

  1. 编译/解析阶段: js 执行前会有一个解析过程,会检查语法,创建全局执行上下文,并对函数进行预编译,初始化一些代码执行时所用到得变量进行变量提升.当访问一个变量时,会到当前执行上下文中的作用域链上查找.
  2. 执行阶段: 按照代码顺序依次执行同步任务,同步任务完成后按照流程执行异步任务

js 为什么要有变量提升

  • 优点:

    1. 提高性能: 解析和预编译过程中的声明提升可以提高性能,让函数可以在执行时预先为变量分配栈空间
    2. 容错性更好: 声明提升还可以提高 JS 代码的容错性,使一些不规范的代码也可以正常执行
  • 在什么阶段进行的变量提升: js 执行流程的编译阶段

变量提升(var let const function class)

暂时性死区:当程序的控制流程在新的作用域(module function 或 block 作用域)进行实例化时,在此作用域中用 let/const 声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定, 所以是不能被访问的,如果访问就会抛出 uninitialized 错误。因此,在这运行流程进入作用域提升变量,到执行声明变量这行代码之间的这一段时间,就称之为暂时死区。

  • ps:
  1. ES5 中作用域有:全局作用域 函数作用域

  2. ES6 中增加的块级作用域(块作用域由 { } 包括,if 语句和 for 语句里面的{ }也属于块作用域)

  3. 变量提升,具名创建的函数(不包含通过 var 声明的匿名函数等)会优先于 var let const 之前进行变量提升

    javascript
    复制代码
    function fn(){
      console.log(a)
      var a=2;
      function a(){}
      console.log(a)
    }
    fn(1)
    
    // 变量提升后
    
     function fn(){
      function a(){}
      var a;
      console.log(a)   // fn
      a=2;
      console.log(a)   // 2
    }
    fn(1)
    
  • 总结:
  1. var 定义的变量,没有块级作用域特性。变量提升后,在执行上下文的变量环境下初始化值为 undefined
  2. let 定义的变量,具有块级作用域特性。变量提升后,在执行上下文的词法环境下初始化值为未初始化 uninitialized。不可声明多次同变量名称的变量。
  3. const 用来定义常量,具有块级作用域特性。变量提升后,在执行上下文的词法环境下初始化值为未初始化 uninitialized。const 声明时必须赋值。const 赋值后的基本类型变量不可能修改、复杂类型不可修改的是其存储在栈中的指针。在一个全局环境下(即在一个 html 文件下的所有 js 代码中)不能声明多次
  4. class 创建类,具有块级作用域特性。变量提升后,在执行上下文的词法环境下初始化值为未初始化 uninitialized
  5. function 具名函数创建下,没有块级作用域特性。变量提升后,在执行上下文的变量环境下初始化值为 undefined

执行上下文

  • ps: 创建执行上下文的过程中,也是指定此执行上下文 this 的过程(因为箭头函数不会创建执行上下文,所以其没有 this 指向,会沿用上层的 this 指向)

  • 执行上下文的类型

    1. 全局执行上下文 -- 任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
    2. 函数执行上下文 -- 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(后进先出)执行一系列步骤。
    3. Eval 函数执行上下文 -- 执行在 eval 函数内部的代码也会有它属于自己的执行上下文

执行栈 : 是一种拥有 LIFO(后进先出)数据结构的栈,被用来存储/管理代码运行时创建的所有执行上下文。

例如 : 当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。

sql
复制代码
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
  • 创建执行上下文流程 : 有两个阶段 创建阶段 和 执行阶段

    • 在 JavaScript 代码执行前,执行上下文将经历创建阶段。在创建阶段会发生三件事:

      1. this 值的决定,即我们所熟知的 This 绑定。
      2. 创建词法环境组件。
      3. 创建变量环境组件。
      ini
      复制代码
        ExecutionContext = {
          ThisBinding = <this value>,
          LexicalEnvironment = { ... },
          VariableEnvironment = { ... },
        }
      
      yaml
      复制代码
        let a = 20;
        const b = 30;
        var c;
      
        function multiply(e, f) {
         var g = 20;
         return e * f * g;
        }
      
        c = multiply(20, 30);
        // -----------  执行上下文的样子 --------------
        GlobalExectionContext = {
      
          ThisBinding: <Global Object>,
      
          LexicalEnvironment: {
            EnvironmentRecord: {
              Type: "Object",
              // 在这里绑定标识符   let const被放在词法环境下,与var不同,所以其会出现暂时性死区,因为开始的变量提升let const的值是未初始化
              a: < uninitialized >,
              b: < uninitialized >,
            }
            outer: <null>
          },
      
          VariableEnvironment: {
            EnvironmentRecord: {
              Type: "Object",
              // 在这里绑定标识符   var被放在变量环境下,所以变量提升后的初始化值是undefined
              c: undefined,
              multiply: < func >
            }
            outer: <null>
          }
        }
      
        FunctionExectionContext = {
          ThisBinding: <Global Object>,
      
          LexicalEnvironment: {
            EnvironmentRecord: {
              Type: "Declarative",
              // 在这里绑定标识符
              Arguments: {0: 20, 1: 30, length: 2},
            },
            outer: <GlobalLexicalEnvironment>
          },
      
          VariableEnvironment: {
              EnvironmentRecord: {
                Type: "Declarative",
                // 在这里绑定标识符
                g: undefined
              },
              outer: <GlobalLexicalEnvironment>
          }
        }