JavaScript深入--->执行上下文栈

225 阅读3分钟

执行上下文栈

JavaScript代码执行顺序是同步的

    var foo = function(){
        console.log('foo1')
    }
    foo(); // foo1
    var foo = function(){
        console.log('foo2')
    }
    foo(); // f002

再看看下面的代码有什么不同

    function foo(){
        console.log('foo1');
    }
    foo(); // foo2
    function foo(){
        console.log('foo2');
    }
    foo(); // foo2

看出什么不同了吗?JavaScript引擎执行代码是一段一段的,不是一行一行执行的。

当一段执行完毕后,进行下一段的'准备工作'。

'准备工作' 也叫做 预编译 分为 变量提升 函数声明提升。

可执行代码

这里就讲讲什么是可执行代码:

可执行代码就是指将目标代码连接后形成的代码,简单来说就是机器能直接执行的代码。

那可执行代码分几种类型呢?

分为三种:全局代码、函数代码、eval代码

  • 全局代码:作为JavaScript Program 处理的源代码文本。
  • 函数代码:作为FunctionBody 被解析的的源代码文本。
  • eval代码:提供给eval内置函数的源代码文本。

这里就不细说可执行代码的深层知识,喜欢可以关注,后续会更新发布有关JavaScript的相关知识干货。

上面讲到'准备工作',到底'准备工作'是什么时候进行的呢?

举个例子,当代码执行的时候,就会进行准备工作,这里的'准备工作',专业点来说,就是叫做"执行上下文(Execution Context)"

执行上下文栈

每次代码执行都会创建一个执行上下文,那么我们如何管理呢?

JavaScript引擎有一个"执行上下文栈(Execution context stack , ECS)"来帮助你管理。

现在我么模拟一下执行上下文栈,这里定义执行上下文栈是一个数组。

    ECStack = [];+

JavaScript引擎开始解析代码的时候,最先遇到的是全局代码,所以初始化 执行上下文栈中添加一个名字叫globalContext的全局执行上下文,而且只有程序结束运行,ECStackcai'hui被清空,所以ECStack结束前,ECStack最顶部都会有一个globalContext。

    ECStack = [
        globalContext
    ];

现在看看这段代码:

    function foo3(){
        console.log('foo3');
    }
    function foo2(){
        foo3();
    }
    function foo1(){
        foo2();
    }
    foo1();

当执行了一个函数,就会创建一个执行上下文,并且添加到执行上下文栈中,当函数执行完毕,就会将该函数的执行上下文从执行上下文栈中删除,按照这样的工作原理,我们看看JavaScript引擎怎么处理上面这段代码。

    // 执行foo1(),创建foo1()的执行上下文
    ECStack.push(foo1<functionContext>);
    // ECStack = [globalContext,foo1<functionContext>];
    
    // foo1()中执行了foo2(),创建foo2()的执行上下文
    ECStack.push(foo2<functionContext>);
    // ECStack = [globalContext,foo1<functionContext>,foo2<functionContext>];
    
    // foo2()中执行了foo3(),创建foo3()的执行上下文
    ECStack.push(foo3<functionContext>);
    // ECStack = [globalContext,foo1<functionContext>,foo2<functionContext>,foo3<functionContext>];
    
    // foo3()执行结束,删除foo3()的执行上下文
    ECStack.pop();
    // ECStack = [globalContext,foo1<functionContext>,foo2<functionContext>];
    
    // foo2()执行结束,删除foo2()的执行上下文
    ECStack.pop();
    // ECStack = [globalContext,foo1<functionContext>];
    
    // foo1()执行结束,删除foo1()的执行上下文
    ECStack.pop();
    // ECStack = [globalContext];
    
    // 关闭程序,清空ECStack
    ECStack.pop();
    // ECStack = [];

执行上下文

  • 作用域链 scope
  • 变量对象 GO AO (预编译)
  • this 执行上下文一旦删除,以上三个内容都会被删除

最后小例子

    var scope = 'global scope';
    function checkscope(){
        var scope = 'local scope';
        function f(){
            return scope;
        }
        return f();
    }
    checksope();
    var scope = 'global scope';
    function checkscope(){
        var scope = 'local scope';
        function f(){
            return scope;
        }
        return f;
    }
    checksope()();

两段代码执行结果一样,到底两段代码有什么不同呢?

大家看完这篇文章应该都能想到,是执行上下文栈的变化不一样

模拟一下两段的执行上下文栈的变化

    ECStack.push(checkscope<functionContext>);
    ECStack.push(f<functionContext>);
    ECStack.pop();
    ECStack.pop();
    ECStack.push(checkscope<functionContext>);
    ECStack.pop();
    ECStack.push(f<functionContext>);
    ECStack.pop();

今天的分享就到此为止啦!喜欢可以关注点赞走一波,谢谢大家!