• 博客
  • 项目
  • 碎碎念
  • 留言板
  • 关于我
js 引擎执行过程:运行机制,语法分析,创建函数调用栈,创建上下文,创建作用域链
更新时间:2025/09/04 分类:编程技术

尝鲜

下面两段代码的执行结果

console.log(person);
console.log(fun);
var person = 'koa';
console.log(person);

function fun() {
  console.log(person);
  var person = 'express';
  console.log(person);
}
fun();
console.log(person);
// 结果
// undefined
// 函数
// koa
// undefined
// express
// koa

console.log('step1');

function first() {
  console.log('step2');
  second();
  console.log('step3');
}

function second() {
  console.log('step4');
}
first();
console.log('step5');
// 结果
// step1
// step2
// step4
// step3
// step5

运行机制

js 执行是单线程的,是基于事件循环的,事件循环分为下面几个步骤

  1. 所有同步任务都在主线程上执行,形成一个执行栈
  2. 主线程之外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件
  3. 一旦执行栈中所有的同步任务执行完毕,系统就会读取任务队列,看看有哪些事件,那么对应的异步任务结束等待状态,进入执行栈,开始执行
  4. 主线程不断重复第三步

主线程的执行过程就是一个 tick,所有的异步结果都是通过任务队列来调度,消息队列中存放着一个个 task,task 分为 macro task 和 micro task,常见的 macro task 分为 setTimeout,MessageChannel,postMessage,setImmediate,常见的 micro task 有 MutationObserver 和 Promise.then

执行过程

语法分析

分析 js 代码块语法是否正确,不正确抛出语法错误,停止该 js 代码块的执行,然后加载下一个代码块,语法正确,则进入预编译阶段

预编译阶段

js 运行环境

  • 全局环境

    js 代码块加载完毕,并且没有语法错误,则进入全局环境

  • 函数环境

    函数调用执行时,进入函数环境,不同函数则函数环境不同

函数调用栈:先进后出,后进先出

每次进入不同环境,会创建一个对应的上下文,在一段 js 程序中,会创建多个执行上下文,js 引擎会创建一个函数调用栈对这些执行上下文进行处理,栈底永远是全局执行上下文,栈顶永远是当前执行上下文

  1. function bar() {
      var name = 'koa';
    
      function foo() {
        var age = '22';
      }
      foo();
    }
    bar();

  2. 进入全局环境,创建全局执行上下文
  3. 调用 bar 函数,进入 bar 函数运行环境,创建 bar 函数上下文
  4. 在 bar 函数内部调用 foo 函数,进入 foo 函数运行环境,创建 foo 函数执行上下文
  5. 栈底是全局执行上下文,栈顶是 foo 函数执行上下文,foo 函数内部没有再调用其他函数,进行出栈操作
  6. foo 函数执行完毕后,栈顶 foo 执行上下文,首先出栈
  7. bar 函数执行完毕后,bar 函数执行上下文出栈
  8. globar 在浏览器或者标签页关闭时候出栈

创建执行上下文

在全局环境中,window 对象就是全局执行的上下文变量对象,所有变量和函数都是 window 对象的属性或方法,函数声明提前和变量声明提升时古在创建变量对象中进行的,函数声明优先级高于变量声明



// 创建上下文
function fun(a, b) {
  var num = 1;

  function test() {
    console.log(num);
  }
}
fun(2, 3); <
!--创建的结果-- >
funEC = {
  //变量对象
  VO: {
    //arguments对象
    arguments: {
      a: undefined,
      b: undefined,
      length: 2
    },
    //test函数
    test: < test reference > ,
    //num变量
    num: undefined
  },
  //作用域链
  scopeChain: [],
  //this指向
  this: window
};


创建变量对象
  • 创建 arguments 对象,建立该对象的属性与属性值,仅在函数环境中执行,箭头函数除外,全局环境没有此过程
  • 检查当前上下文的函数声明,按照代码顺序查找,将找到函数提前声明,如果当前上下文的变量对象没有该函数名属性,则需要在该变量对象以函数名创建一个属性,属性值为指向该函数所在堆内存地址的引用,如果引用存在,会被覆盖
  • 查当前上下文的变量声明,将找到的变量提前声明,如果没有变量属性,则为该变量创建一个属性,属性值为 undefined,如果存在,忽略变量声明
创建作用域链

作用域链的第一项永远是当前作用域,就是 innerTest,最后一项永远是全局作用域,查找方式是从左到右,直到找到全局作用域,如果再找不到,就报错

  • 作用域链由当前执行环境的变量对象与上层环境的活动对象组成,保证变量的权限访问和函数有序访问
  • // 创建作用域链
    var num = 30;
    
    function test() {
      var a = 10;
    
      function innerTest() {
        var b = 20;
        return a + b;
      }
      innerTest();
    }
    test();
    // 创建结果
    innerTestEC = {
      //变量对象
      VO: {
        b: undefined
      },
      //作用域链
      scopeChain: [VO(innerTest), AO(test), AO(global)],
      //this指向
      this: window
    };
确定 this 指向
在全局环境中,全局执行上下文中变量对象的 this 是指向 window 的
js运行机制
联系我