前言
前段时间看了一篇关于作用域和作用域链的文章,一个比较陌生的词出现在我眼前——词法环境或者是词法作用域。词法环境或者词法作用域到底跟作用域有什么关系呢?下面就介绍一下什么是作用域以及什么是作用域链。
什么是词法环境/词法作用域?
官方规范对词法环境的说明是:词法环境(Lexical Environments)是一种规范类型,用于根据ECMAScript代码的词法嵌套结构来定义标识符与特定变量和函数的关联。
通俗来说,词法环境就是一套约定好的规则,我们在写代码的时候遵守这个规则就好了。
其实词法环境就是作用域
所以我们所说的作用域其实就是一套规则,这套规则用于管理JS引擎如何在当前作用域以及嵌套的子作用域里根据标识符进行变量的查找。
作用域
每个JavaScript函数都是一个对象,对象中有些属性是我们可以访问,有些不可以,这些属性仅供JavaScript引擎存取,作用域(Scope)就是其中一个。、
JS的函数作用域,将作用域拆开来看,“作用”表示读写操作,函数可以读取代码,改写代码;“域”表示空间,范围,区域。一般指约定好的函数中变量或者函数中的函数的使用权限。
JavaScript有全局作用域和函数作用域,其中存储了运行期执行上下文的集合。
那作用域什么时候产生呢?——函数在调用激活时,会开始创建对应的执行上下文,在执行上下文生成的过程中,变量对象,作用域链,以及this的值会分别被确定。执行上下文的创建过程:
执行上下文其实是这样子的
test ECStack={
vo:{
argument:{...},
//以及其他的引用eg:
foo:引用地址
this:确定的this指向
}
scopeChain:...//作用域链
}
作用域链
了解了执行上文的生成过程以及执行上下文的样子,那到底作用域链是什么样子的呢?下面会通过一个栗子来介绍作用域链——作用域中所存储的执行器上下文对象的集合,这个集合呈链接式链接,我们把这种链式链接叫作用链。
举一个小栗子:
var a = 1;
function test() {
var b = a ;
function innerTest() {
var c = a+b;
return b + c;
}
return innerTest();
}
test();
用代码可以这样表示innerTest()的作用域链
innerTest ECStack={//这个是执行上下文,这里写出来只是让你时刻记住在生成执行上下文的时候会确定作用域链
vo:{...}//变量对象
//***作用域链***
scopeChain:[VO(innerTest),VO(test),VO(global)]
}
作用域链:
从图中可以看出已经完成变量赋值,即执行上下文的生命周期已经到执行阶段,这这时变量对象(VO)就会变为活动对象(AO)。这就是为什么图中test和innerTest不是表示为变量对象(VO)的原因。
其他的解释 变量对象VO是与执行上下文相关的特殊对象,用来存储上下文的函数声明,函数形参和变量。在global全局上下文中,变量对象也是全局对象自身,在函数上下文中,变量对象被表示为活动对象AO。
结论JS引擎在查询标识符(变量名)时就是通过这个单向通道查询的,这大概是全局对象不能访问局部对象的原因了。