理解javascript作用域与作用域链

如果不明白VO/AO可以先看一看上一个博客内容:理解javascript执行环境(执行上下文)


正文内容

作用域
- 一块代码可执行区域,在该区域中的变量和函数在区域外部是不可见的

作用域链
- 由多个执行上下文的变量对象构成的链表就叫做作用域链。
- 当一个函数被创建时会默认添加一个名为[[Scope]]的特殊内部属性,如果该函数时一个全局函数,那么在该函数被创建时会预先创建一个包含全局变量对象(全局VO)的作用域链,这个作用域链被保存在该函数的[[Scope]]属性中,当该函数调用时,会为该函数创建一个执行环境,通过复制[[Scope]]属性中的对象构建起执行环境作用域链,之后又为该函数创建一个活动对象(AO)并推入执行环境作用域链的前端。如果该执行环境内部还有子函数,那么将执行环境作用域链保存到子函数的[[Scope]]属性中,以便子函数调用时取用

作用域链的创建

第一步 当程序执行时创建全局作用域和全局VO对象,发现fn1与fn2函数是在全局环境下定义,那么在创建fn1与fn2函数时预先创建一个包含全局VO对象指针的作用域链,保存到这两个函数各自的[[Scope]]属性当中,

理解javascript作用域与作用域链_第1张图片
第二步,当程序执行到fn1()调用,程序执行流进入fn1函数体中,创建fn1执行环境,并创建fn1的AO对象,之后将AO对象的指针添加到fn1.[[Scope]]中作用域链的前端

理解javascript作用域与作用域链_第2张图片
第三步,程序在fn1中继续执行,发现函数child是定义在fn1的执行环境中的,那么将fn1.[[Scope]]中保存的作用域链复制给child.[[Scope]],这时fn1.[[Scope]]中保存的作用域链应该是Scope -> fn1 AO -> Global VO

第四步,发现child()函数调用,程序进入child函数中,创建环境与AO对象,并将AO对象添加到child.[[Scope]]中作用域链的前端
理解javascript作用域与作用域链_第3张图片
第五步,程序退出child函数并将其销毁,退出fn1函数并将其销毁,发现fn2()函数调用,进入fn2函数体内部,进行与fn1类似的操作

作用域链的访问

拿child函数来举例
1. 程序进入child函数体内部,查询child环境下的AO对象,没有发现a或b变量
2. 向后查找作用域链,发现fn1环境下的AO对象有变量b,但是没有变量a
3. 继续沿着作用域链向后查找,发现全局环境下的VO对象中包含着变量a
4. 取得a和b的值进行加法操作并将结果赋值给c
5. 全局VO永远处于作用域链的最末端,如果在全局VO中仍然没有找到需要的数据,那么浏览器将会报错
6. 作用域链 -> child AO -> fn1 AO -> Global VO

参考资料
- JavaScript高级程序设计(第3版)第七章-179页

你可能感兴趣的:(javascript,javascript,web)