【深入JavaScript日记五】(阶段总结向)利用现有知识深入分析执行上下文

前言

在之前的部分里,我们花费了大量的力气,终于算是学习明白了每个执行上下文的三个重要属性:变量对象(Variable object,VO),作用域链(Scope chain),this,现在我们可以试着结合之前的上下文来更加深入地分析一下代码的执行过程了。


正文内容

抛出问题

copy过来一道现成的题目

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();			//差别在这里1
}
checkscope();			//差别在这里2
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;			//差别在这里1
}
checkscope()();			//差别在这里2

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

详细分析

首先分析一下第一段

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

①首先,执行全局代码,创建全局执行上下文,并将全局上下文压入执行上下文栈

    ECStack = [
        globalContext
    ];

全局上下文初始化

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

与此同时,checkscope 函数被创建,保存作用域链到函数的内部属性[[scope]]

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

③执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈

    ECStack = [
        checkscopeContext,
        globalContext
    ];

checkscope 函数执行上下文初始化

  • 将checkscope 函数的作用域链复制到checkscope 函数上下文的Scope里
    checkscopelContext = {
        Scope: [globalContext.VO]		//将函数作用域链搬到函数执行上下文里
    }
  • 在函数上下文里用 arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明,最后将活动对象压入 checkscope 作用域链顶端。
    checkscopelContext = {
    	AO: {
        	arguments: {			//用 arguments 创建活动对象
            	length: 0
        	},
        	scope: undefined				//加入形参、函数声明、变量声明
        	f: reference to function f(){}
    	}
        Scope: [AO, globalContext.VO],//将活动对象压入 checkscope 作用域链顶端
		
    }

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

    ECStack = [
        globalContext
    ];

⑥ 执行 f 函数,创建 f 函数执行上下文,f 函数执行上下文被压入执行上下文栈

    ECStack = [
        fContext,
        globalContext
    ];

⑦ 函数f 上下文初始化,与第④步相同(大佬可以跳过,我不是很熟悉再过一遍)

  • 首先将函数作用域链复制到上下文里
    fContext = {
        Scope: [ checkscopeContext.AO, globalContext.VO],
    }
  • 然后用 argument 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明,最后将活动对象压入 f 作用域链顶端。
    fContext = {
    	AO:{
			argument:{
				length:0;
			}
		}
        Scope: [AO, checkscopeContext.AO, globalContext.VO],
        this: undefined
    }

⑧ f 函数执行,沿着作用域链查找 scope 值,返回 scope 值
⑨ f 函数执行完毕,f 函数上下文从执行上下文栈中弹出

    ECStack = [
        globalContext
    ];

这样下来,我们终于可以从原理的角度来解读一段代码了,但是我们依然看不出来这两个例子的不同之处,那就继续学习后面的知识吧。


总结

学习到这里,我们算是学习了较为完整的一部分知识,理解之后能解释一些原来无法理解的执行顺序,让我们开发起来更加得心应手与规范高效。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AntyRia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值