JS作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。 ——《Javascript高级程序设计》
作用域(scope)指的是变量存在的范围。在 ES5 的规范中,Javascript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。要了解作用域,需要先了解执行环境的相关概念。
执行环境
执行环境( execution context,简称“环境” )是JavaScript中的重要概念之一。执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
全局执行环境是最外围的一个执行环境。在Web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境知道应用程序退出–例如关闭网页或浏览器—时才会被销毁)
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行后,栈将其环境弹出,把控制权返回给之前的执行环境。
执行环境的建立分为两个阶段:进入执行上下文(创建阶段)和执行阶段(激活/执行阶段)
- 进入上下文阶段:发生在函数调用时,但在执行具体代码之前。具体完成创建作用域链;创建变量、函数和参数以及求this的值
-
执行代码阶段:主要完成变量赋值、函数引用和解释/执行其他代码
总的来说可以将执行上下文看作是一个对象
变量对象
每一个执行环境都对应一个变量对象(variable object),在该执行环境中定义的所有变量和函数都存放在其对应的变量对象中。
(1)进入执行上下文时,变量对象的初始化过程如下:
函数的形参:变量对象的一个属性,其属性名就是形参的名字,其值就是实参的值;对于没有传递的参数,其值为undefined;
函数声明:变量对象的一个属性,其属性名和属性值都是函数对象创建出来的,如果变量对象已经办好了相同名字的属性,则替换它的值
变量声明:变量对象的一个属性,其属性名即为变量名,其值为undefined;如果变量名和已经声明的函数名或者函数的参数名,则不会影响已经存在的属性
(2)执行代码阶段,变量对象中的一些属性undefined值将会确定
活动对象
当函数被调用的时候,一个特殊的对象–活动对象将会被创建。这个对象中包含形参和arguments对象。活动对象之后会作为函数上下文的变量对象来使用。换句话说,活动对象除了变量和函数声明之外,它还存储了形参和arguments对象。
作用域链
已知,当函数被调用时,会创建一个变量对象的作用域链。而首先加入的变量对象是当前函数的活动对象,即形参和arguments。下一个变量对象来自包含环境,即该函数所属环境,在下一个变量对象来自下一个包含环境。这样一直延伸到全局执行环境,全局执行环境的变量对象始终都是作用域链中的最后一个对象。