JavaScript 中作用域的理解

执行上下文(Execution Context)

每当控制器转到可执行代码的时候,就会进入一个可执行上下文,就是当前代码的执行环境,它会形成一个作用域。

javascript中的执行环境包含三种:

  • 全局环境
  • 函数环境
  • eval

执行上下文的生命周期

当调用一个函数时,一个新的执行上下文会被创建,它包含两个阶段:

  • 创建阶段

    在这个阶段,执行上下文会创建变量对象(VO),作用域链(scopeChain),确定this指向

  • 代码执行阶段

    创建完成之后就开始执行代码,这个时候会完成变量赋值,函数引用,以及执行其他代码

变量对象(Variable Object)

变量对象的创建依次经历:

  1. 建立arguments对象,
  2. 检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中,以函数名创建一个属性,属性值为指向该函数所在的内存地址,如果函数名的属性已经存在,则覆盖。
  3. 减产当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过

作用域链

当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。

函数创建

函数的作用域在函数创建的时候就决定了

这是因为函数内部有一个[[scope]]的内部属性,当函数创建的时候,就会保存所有父变量对象到其中

function foo(){
    function bar(){
        ...
    }
}

函数创建的时候各自的[[scope]]为:

foo.[[scope]] = [
    globalContext.VO
]
bar.[[scope]] = [
    fooContext.AO,
    globalContext.VO,
]

案例

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

执行过程如下:

  1. 创建checkscope函数,保存作用域链到内部属性[[scope]]中

    checkscope.[[scope]] = [
        globalContext.VO
    ]
    
  2. 执行checkscope函数,创建checkscope函数执行上下文,checkscope函数执行上下文被压入执行上下文栈

    ECStack = [
        checkscopeContext,
        globalContext
    ]
    
  3. checkscope函数不立即执行,开始准备工作。第一步:复制函数[[scope]]属性创建作用域链,用arguments创建变量对象VO,随后初始化变量对象,加入形参,函数声明,变量声明

    checkscopeContext = {
        VO:{
            arguments: {
                length: 0
            },
            foo: undefined
        },
        scope: checkscope.[[scope]]
    }
    
  4. 第二步:激活VO变成活动对象AO

    checkscopeContext = {
        AO:{
            arguments: {
                length: 0
            },
            foo: undefined
        },
        scope: checkscope.[[scope]]
    }
    
  5. 第三步:将AO压入checkscope函数作用域链顶端

    checkscopeContext = {
        AO:{
            arguments: {
                length: 0
            },
            foo: undefined
        },
        scope: [AO,checkscope.[[scope]]]
    }
    
  6. 准备工作做完,开始执行函数,随着函数的执行,修改AO的属性值

    checkscopeContext = {
        AO:{
            arguments: {
                length: 0
            },
            foo: "foo"
        },
        scope: [AO,checkscope.[[scope]]]
    }
    
  7. 查找到foo的值,返回后函数执行完毕,checkscope执行上下文从执行上下文栈中弹出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值