JavaScript深刻之执行上下文

JavaScript深刻系列第七篇,结合以前所讲的四篇文章,以权威指南的demo为例,具体讲解当函数执行的时候,执行上下文栈、变量对象、做用域链是如何变化的。git

前言

《JavaScript深刻之执行上下文栈》中讲到,当 JavaScript 代码执行一段可执行代码(executable code)时,会建立对应的执行上下文(execution context)。github

对于每一个执行上下文,都有三个重要属性:面试

  • 变量对象(Variable object,VO)闭包

  • 做用域链(Scope chain)app

  • this函数

而后分别在《JavaScript深刻之变量对象》《JavaScript深刻之做用域链》《JavaScript深刻之从ECMAScript规范解读this》中讲解了这三个属性。ui

阅读本文前,若是对以上的概念不是很清楚,但愿先阅读这些文章。this

由于,这一篇,咱们会结合着全部内容,讲讲执行上下文的具体处理过程。code

思考题

《JavaScript深刻之词法做用域和动态做用域》中,提出这样一道思考题:对象

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

两段代码都会打印'local scope'。虽然两段代码执行的结果同样,可是两段代码究竟有哪些不一样呢?

紧接着就在下一篇《JavaScript深刻之执行上下文栈》中,讲到了二者的区别在于执行上下文栈的变化不同,然而,若是是这样笼统的回答,依然显得不够详细,本篇就会详细的解析执行上下文栈和执行上下文的具体变化过程。

具体执行分析

咱们分析第一段代码:

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

执行过程以下:

1.执行全局代码,建立全局执行上下文,全局上下文被压入执行上下文栈

ECStack = [
        globalContext
    ];

2.全局上下文初始化

globalContext = {
        VO: [global, scope, checkscope],
        Scope: [globalContext.VO],
        this: globalContext.VO
    }

2.初始化的同时,checkscope 函数被建立,保存做用域链到函数的内部属性[[scope]]

checkscope.[[scope]] = [
      globalContext.VO
    ];

3.执行 checkscope 函数,建立 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈

ECStack = [
        checkscopeContext,
        globalContext
    ];

4.checkscope 函数执行上下文初始化:

  1. 复制函数 [[scope]] 属性建立做用域链,

  2. 用 arguments 建立活动对象,

  3. 初始化活动对象,即加入形参、函数声明、变量声明,

  4. 将活动对象压入 checkscope 做用域链顶端。

同时 f 函数被建立,保存做用域链到 f 函数的内部属性[[scope]]

checkscopeContext = {
        AO: {
            arguments: {
                length: 0
            },
            scope: undefined,
            f: reference to function f(){}
        },
        Scope: [AO, globalContext.VO],
        this: undefined
    }

5.执行 f 函数,建立 f 函数执行上下文,f 函数执行上下文被压入执行上下文栈

ECStack = [
        fContext,
        checkscopeContext,
        globalContext
    ];

6.f 函数执行上下文初始化, 如下跟第 4 步相同:

  1. 复制函数 [[scope]] 属性建立做用域链

  2. 用 arguments 建立活动对象

  3. 初始化活动对象,即加入形参、函数声明、变量声明

  4. 将活动对象压入 f 做用域链顶端

fContext = {
        AO: {
            arguments: {
                length: 0
            }
        },
        Scope: [AO, checkscopeContext.AO, globalContext.VO],
        this: undefined
    }

7.f 函数执行,沿着做用域链查找 scope 值,返回 scope 值

8.f 函数执行完毕,f 函数上下文从执行上下文栈中弹出

ECStack = [
        checkscopeContext,
        globalContext
    ];

9.checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出

ECStack = [
        globalContext
    ];

第二段代码就留给你们去尝试模拟它的执行过程。

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

不过,在下一篇《JavaScript深刻之闭包》中也会说起这段代码的执行过程。

下一篇文章

《JavaScript深刻之闭包》

相关连接

《JavaScript深刻之词法做用域和动态做用域》

《JavaScript深刻之执行上下文栈》

《JavaScript深刻之变量对象》

《JavaScript深刻之做用域链》

《JavaScript深刻之从ECMAScript规范解读this》

重要参考

《一道js面试题引起的思考》

本文写的太好,给了我不少启发。感激涕零!

深刻系列

JavaScript深刻系列目录地址:https://github.com/mqyqingfeng/Blog

JavaScript深刻系列预计写十五篇左右,旨在帮你们捋顺JavaScript底层知识,重点讲解如原型、做用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。

若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎star,对做者也是一种鼓励。

相关文章
相关标签/搜索