JS 作用域链 学习记录

看完冴羽大大写的作用域链分析的文章脑子还是有点迷糊,在此再针对其中的 “函数执行上下文中作用域链和变量对象的创建过程” 帮助自己重新做一份梳理和学习记录。

1. 执行上下文

  当JS代码执行到可执行函数时,即运行到执行函数的地方,会创建其对应的执行上下文
  每个执行上下文都有三个非常重要的属性,即:

  • 变量对象(VO) (函数执行时的活动对象: AO)
  • 作用域链(Scope Chain)
  • this
2. 什么是作用域链?

    JS在查找变量时,会先从当前执行上下文的变量对象中查找,如果没找到,则从父级执行上下文的变量对象中查找,依次向上递推,一直找到全局上下文的变量对象为止。这样由多个执行上下文的变量对象构成的链表就叫做作用域链

3. JS中作用域链的创建和变化过程

JS中作用域链的创建和变化分为两个时期: 函数创建时期函数激活时期

  • 3.1 函数创建时期

        由于JS采用的是静态作用域(词法作用域),所以函数的作用域在函数定义时就已经决定了,而与函数调用位置无关。
        导致上述现象的原因与JS函数的一个内置属性 [[scope]] 有关。每一个函数创建后(定义后),其内部的[[scope]] 都会将该函数所有的父变量对象(VO)保存到其中。形成父变量对象的一个层级链。但是,这并不是完整的作用域链 !

    function foo(){
    	function bar(){
    	
    	}
    }
    
    这两个函数创建时,其内部各自的[[scope]]是这样的,[[scope]]中保存各自所有的父变量对象:
    
    foo.[[scope]] = [
    	globalContext.VO
    ]
    
    bar.[[scope]] = [
    	fooContext.AO,
    	globalContext.VO
    ]
    
  • 3.2 函数激活时期

        函数激活,即开始执行函数的时候,会进入函数上下文,创建该函数的变量对象(VO/AO)之后,将该变量对象添加到作用域链的前端。形成最终完整的作用域链。
        这时执行上下文完成的作用域链我们称为Scope:

    Scope = [AO].concat([[Scope]])   // 数组拼接
    
4.通过一个小Demo分析完整作用域链的形成过程。
var scope = "globalScope";
function checkScope(){
	var scope2 = "localScope";
	return scope2;
}

checkScope();

分析执行过程:

  1. checkScope函数被定义的时候即 创建checkScope函数,
    checkScope内置的[[scope]]属性会保存其所有父级VO,这里就是全局执行上下文的VO

    checkScope.[[scope]] = [
    	globalContext.VO
    ]
    
  2. 调用checkScope函数的时候,会形成其对应的执行上下文,checkScope函数被压入执行上下文栈(如果checkScope函数里面还调用了其他函数,那些函数也会被依次压入执行上下文栈):

    ECStack = [
    	(push)  checkScopeContext, 
    	globalContext
    ]
    
  3. 此时checkScope函数不会立即从栈中pop出来执行。 而是会先进行一些**“准备工作”**:

    第一步: 复制checkScope函数创建时的父级VO作用域链,作为对象保存到其执行上下文对象中:

    // check
    checkScopeContext = {
    	Scope: checkScope.[[scope]]
    }
    

    第二步: 生成checkScope自己的变量对象AO: 用arguments创建活动对象,随后初始化活动对象,加入形参、函数声明以及变量声明:

    AO: {
    	arguments: [
    		length: 0
    	],
    	scope: undefined
    }  // 这是新创建的AO
    

    第三步: 将新创建的AO压入原来的作用域顶端形成完整的作用域链:

    checkscopeContext = {
    	AO: {
            arguments: {
                length: 0
            },
            scope2: undefined
        },  // checkScope的活动变量
        
        Scope: [AO, [[Scope]]]  // 完整的作用域链
    }
    
  4. 准备工作完成后,开始执行函数。首先根绝代码执行顺序依次修改AO中的属性值。

    checkscopeContext = {
        AO: {
            arguments: {
                length: 0
            },
            scope2: 'local scope'
        },
        Scope: [AO, [[Scope]]]
    }
    
  5. 执行函数时将checkScopeContext从上下文执行栈ECStack中弹出。函数执行完毕。

    ECStack = [
    	(pop) checkScopeContext,
    	globalContext
    ]
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值