JavaScript函数作用域链

171 阅读2分钟

学习JavaScript的都知道有原型链,在JavaScript还存在一种链叫做函数作用域链,当查找变量的时候会先从当前上下文变量对象中查找,如果没有找到的话,就会从父级的执行上下文变量对象中查找,一直会找到全局上下文变量对象。接下来我们就来分析一下作用链的形成过程。

作用域类型

编程语言都有作用域一说,但是不同的编程语言对应的作用域类型可能是不同的。作用域类型分为两种:

  • 词法作用域:指函数在定义时确定作用域
  • 动态作用域:指函数的作用域在执行的时候确定

我们通过一段代码来说明一下两种作用域:

var scope = 'global scope'
function getScope () {
    var scope = 'function scope'
    return function () {
        console.log(scope)
    }
}
var f = getScope()
f()

对于词法作用域输出结果为'function scope',对于动态作用域输出结果为'global scope';在JavaScript中运行这段代码输出的结果为'function scope',所以我们的JavaScript是属于词法作用域。确定了JavaScript的作用域类型我们下面详细说明一下JavaScript作用域链形成的过程。

函数的创建

上面我们说过JavaScript是词法作用域,函数的作用域在定义的时候就决定了,当函数被创建时会使用函数的内部属性[[scope]]保存父级的作用域链,我们通过一个例子来看一下:

function foo () {
    function boo (){...}
}
foo()

当foo被定义时(全局环境下):

foo.[[scope]] = [globalContext.Vo] // globalContext.Vo为全局作用域

当foo执行时,boo被定义:

boo.[[scope]] = [fooContext.Ao, globalContext.Vo]

函数的激活

当函数被激活时,也就是执行函数时,会将当前函数的上下文添加到作用域链的最前面,这样函数作用链就形成了,下面我们通过例子来一步步说明作用域链的形成过程和具体内容

作用链的具体形成过程

看下面这段代码:

var scope = 'global scope'
function getScope(){
    var scope2 = 'local scope'
    return scope2
}
getScope()

执行过程如下:

  1. getScope函数被创建
getScope.[[scope]] = [globalContext.Vo]
  1. getScope函数执行,创建getScope函数的执行上下文,推入执行上下文栈
ECStack = [getScopeContext, globalContext]
  1. 将getScope函数的[[scope]]属性赋值给getScope执行上下文的scopes属性:
getScopeContext.scope = getScope.[[scope]]
  1. 利用getScope函数的arguments初始化getScopeContext:
getScopeContext = {
    Ao: {
        arguments: {
            length: 0
        },
        socpe2: undefined
    },
    scope: [globalContext.Vo]
}
  1. 将活动对象放到作用域链的最前端
getScopeContext = {
    Ao: {
        arguments: {
            length: 0
        },
        scope2: undefined
    },
    scope: [Ao].concat([globalContext.Vo])
}
  1. 函数执行代码时,修改活动对象Ao的属性值
getScopeContext = {
    Ao: {
        arguments: {
            length: 0
        },
        scope2: 'local scope' // 当执行到var scope2 = 'local scope'时,scope2的值被修改了
    },
    scope: [globalContext.Vo]
}
  1. 函数执行完毕,getScope上下文出栈:
ECStack = [globalContext.Vo]