Skip to content
本页目录

作用域链

在学习什么是作用域链前,需先了解什么是作用域?

作用域

那么什么是作用域呢?有何作用?

作用域是指程序源代码中定义变量的区域,其规定了如何查找变量,确定当前执行代码对变量的访问权限。

作用域类型:

  • 词法作用域:函数的作用域在函数定义的时候决定。
  • 动态作用域:函数的作用域在函数调用的时候决定。

JavaScript就是采用的词法作用域,为了方便理解举个例子:

JavaScript
const who = 'yui'
function kon() {
  console.log(who)
}

function mio() {
  const who = 'mio'
  kon()
}

mio()  // 'yui'

代码执行结果输出'yui',而非'mio',完全符合词法作用域特点;若是动态作用域,此处的输出结果应该是'mio'

作用域链

什么是作用域链呢?

当查找变量的时候,会先从当前上下文的变量对象中查找,若未找到,就会从父级执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。

由于JavaScript是词法作用域,函数的作用域在函数定义的时候就已经被决定。 在函数创建的时候,其内部属性[[scope]]会将所有父级变量对象保存至其中,可以将[[scope]]理解为所有父级变量对象的层级链(并非完整的作用域链)!

作用域链是如何创建和变化的呢?让我们从函数的创建激活切入:

创建

比如上例中,函数konmio各自的[[scope]]为:

JavaScript
mio.[[scope]] = [
  globalContext.VO
]
kon.[[scope]] = [
  mioContext.AO, // 函数用 AO 表示 VO
  globalContext.VO
]

激活

当函数被激活时,进入其上下文,创建VO/AO后,就会将活动对象添加到作用链的前端,此时的作用域链ScopeChain为:

JavaScript
ScopeChain = [AO].concat([[scope]])

作用域链ScopeChain创建完毕。

总结

JavaScript
function kon(name) {
  const fav = '吉太'
  return name + ' fav: ' + fav
}

kon('yui')

总结一下函数执行上下文中作用域链和变量对象的创建过程:

  1. 函数kon被创建并将作用域链保存至[[scope]]
JavaScript
kon.[[scope]] = [
  globalContext.VO
]
  1. 执行kon函数时,创建kon函数的执行上下文,并将kon函数的执行上下文压入ECS
JavaScript
ECS = [
  konContext,
  globalContext
]
  1. kon函数并不会立即执行,开始做准备工作
    3.1. 复制函数[[scope]]属性创建作用域链

    JavaScript
    konContext = {
      Scope: kon.[[scope]]
    }

    3.2. 用arguments创建AO,随后初始化AO(加入形参、函数声明、变量声明)

    JavaScript
    konContext = {
      AO:{
        arguments:{
          0: 'yui',
          length: 1
        },
        fav: undefined
      }
      Scope: kon.[[scope]]
    }

    3.3. 将AO压入kon作用域链顶端

    JavaScript
    konContext = {
      AO:{
        arguments:{
          0: 'yui',
          length: 1
        },
        fav: undefined
      }
      Scope: [AO, [[scope]]]
    }
  2. 准备工作完毕,开始执行函数,随着函数的执行,修改AO的属性值:

JavaScript
konContext = {
  AO:{
    arguments:{
      0: 'yui',
      length: 1
    },
    fav: '吉太'
  }
  Scope: [AO, [[scope]]]
}
  1. 找到fav的值为'吉太',返回后函数执行完毕,函数上下文从执行上下文栈中弹出。
JavaScript
ECS = [
  globalContext
]