js引擎的执行过程---预编译

426 阅读4分钟

前言

本文整理了前端面试高频出现的让你说(猜)打印输出类问题,如果对答案有不一样见解的同学欢迎评论区补充讨论,当然有问题,也欢迎在评论区指出。

引用+参考 www.pianshen.com/article/846…

先来道测试题

  • 如果下面这道题能够正确知道打印顺序,便没必要看这篇文章,省点时间打球、约会去吧
function fn(a, c) {
    console.log(a)  //function a(){}
    var a = 123
    console.log(a) //123
    console.log(c) //function c(){}
    function a() {
    }

    if (false) {
        var d = 678
    }
    console.log(d) //undefined
    console.log(b) //undefined
    var b = function () {
    }
    console.log(b) //function(){}
    function c() {
    }

    console.log(c) //function c(){}
}

fn(1, 2)

全面分析js引擎的执行过程,分为三个阶段

1、语法分析

js的代码块加载完毕之后,会首先进入到语法分析阶段,该阶段的主要作用:

  • 分析该js脚本代码块的语法是否正确,如果出现不正确会向外抛出一个语法错误(syntaxError) ,停止该js代码的执行

2、预编译阶段

通过语法分析阶段之后,语法都正确时,将进入预编译阶段

在分析预编译阶段之前,我们先来了解一下js的运行环境,运行环境主要由三种:

1、全局环境(js代码加载完毕后,进入到预编译也就是进入到全局环境)

2、函数环境(函数调用的时候,进入到该函数环境,不同的函数,函数环境不同)

3、eval环境(不建议使用,存在安全、性能问题)

每进入到一个不同的运行环境都会创建 一个相应的执行上下文(execution context) ,那么在一段js程序中一般都会创建多个执行上下文,js引擎会以栈的数据结构对这些执行进行处理,形成函数调用栈(call stack), 栈底永远是全局执行上下文(global execution context) ,栈顶则永远时当前的执行上下文。

(1)创建函数调用栈

函数调用栈就是使用栈存取的方式进行管理运行环境,特点是先进后出,后进后出

说明:不同的运行环境执行都会进入到代码预编译和执行两个阶段

function parent() {
    var A_context = "parent content";

    function child() {
        var B_context = "child content";
    }

    child()
}
parent()
这里调用栈的顺序:全局执行上下文--parent函数执行上下文--child函数执行上下文

(2)创建执行上下文

也叫做当前的执行环境,创建过程中,主要是做了下面三件事:

1、创建变量对象(variable object,简称VO)

2、创建作用域链(scope chain)

作用域链由当前执行环境的变量对象(未进入到执行阶段前)与上层环境的一系列活动对象(AO)组成,保证了当前执行环境对符合访问权限的变量和函数有序访问。

3、确定this的指向(先忽略)

可参考this指向问题juejin.cn/post/699872…

VO(变量对象)创建过程:

  • 创建arguments对象,检查当前上下文的参数,建立该对象的属性与属性值,仅在函数环境(非箭头函数)中进行的,全局环境没有此过程。

  • 检查当前上下文的函数声明,按照代码顺序查找,将找到的函数提前声明,

    • 如果当前上下文的变量对象没有该函数名属性,则以函数名新建一个属性,属性值则指向该函数所在堆内存地址引用
    • 如果存在,则会被新的引用覆盖掉。
  • 检查当前上下文的变量声明,按照代码顺序查找,将找到的变量提前声明

    • 如果当前上下文的变量对象没有变量名属性,则新建一个属性,属性值为undefined;
    • 如果存在,则忽略该变量声明。
练手
function fun(m,n){
    var name = 'hzy';

    function execution(){
        console.log(name)
    }
}
fun(2,3)执行

创建函数fun的执行上下文
funEC = {
    VO: {//变量对象
        arguments: {//arguments对象
            m: undefined,
            n: undefined,
            length: 2
        },
        //execution函数
        execution: <execution reference>, 
        //函数内变量
        name: undefined
    },
 
    //作用域链
    scopeChain:[VO(execution), AO(fun), AO(global)],
    //this指向
    this: window
}

3、执行阶段

执行阶段另开出新文章详细分析,主要介绍js执行阶段中的同步任务执行和异步任务执行机制(事件循环(Event Loop)

前面问题解决

现在返回看一下

VO: {//变量对象
    arguments: {//arguments对象
        a: undefined,'function a(){}',
        c: undefined,
    },
    //函数内变量
    d:undefined,
    b:undefined,'function () {}',
}
打印顺序:function a(){}(函数覆盖)---123--function c(){}(函数覆盖)--undefined--undefined--function(){}(变量声明) ---function c(){}(函数覆盖)

总结

觉得写得好的,对你有帮助的,可以分享给身边人,知识越分享越多,千万不要吝啬呀

后续更新前端其它知识总结,请关注我,整理好,分享给你们,我们一起学前端