切换主题
作用域链
在学习什么是作用域链前,需先了解什么是作用域?
作用域
那么什么是作用域呢?有何作用?
作用域是指程序源代码中定义变量的区域,其规定了如何查找变量,确定当前执行代码对变量的访问权限。
作用域类型:
- 词法作用域:函数的作用域在函数
定义的时候决定。 - 动态作用域:函数的作用域在函数
调用的时候决定。
JavaScript就是采用的词法作用域,为了方便理解举个例子:
JavaScript
const who = 'yui'
function kon() {
console.log(who)
}
function mio() {
const who = 'mio'
kon()
}
mio() // 'yui'代码执行结果输出'yui',而非'mio',完全符合词法作用域特点;若是动态作用域,此处的输出结果应该是'mio'。
作用域链
什么是作用域链呢?
当查找变量的时候,会先从当前上下文的变量对象中查找,若未找到,就会从父级执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
由于JavaScript是词法作用域,函数的作用域在函数定义的时候就已经被决定。 在函数创建的时候,其内部属性[[scope]]会将所有父级变量对象保存至其中,可以将[[scope]]理解为所有父级变量对象的层级链(并非完整的作用域链)!
作用域链是如何创建和变化的呢?让我们从函数的创建和激活切入:
创建
比如上例中,函数kon和mio各自的[[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')总结一下函数执行上下文中作用域链和变量对象的创建过程:
- 函数
kon被创建并将作用域链保存至[[scope]]
JavaScript
kon.[[scope]] = [
globalContext.VO
]- 执行
kon函数时,创建kon函数的执行上下文,并将kon函数的执行上下文压入ECS
JavaScript
ECS = [
konContext,
globalContext
]kon函数并不会立即执行,开始做准备工作:
3.1. 复制函数[[scope]]属性创建作用域链JavaScriptkonContext = { Scope: kon.[[scope]] }3.2. 用
arguments创建AO,随后初始化AO(加入形参、函数声明、变量声明)JavaScriptkonContext = { AO:{ arguments:{ 0: 'yui', length: 1 }, fav: undefined }, Scope: kon.[[scope]] }3.3. 将
AO压入kon作用域链顶端JavaScriptkonContext = { AO:{ arguments:{ 0: 'yui', length: 1 }, fav: undefined }, Scope: [AO, [[scope]]] }准备工作完毕,开始执行函数,随着函数的执行,修改
AO的属性值:
JavaScript
konContext = {
AO:{
arguments:{
0: 'yui',
length: 1
},
fav: '吉太'
},
Scope: [AO, [[scope]]]
}- 找到
fav的值为'吉太',返回后函数执行完毕,函数上下文从执行上下文栈中弹出。
JavaScript
ECS = [
globalContext
]