JavaScript 深入理解作用域链

第一步. 定义后:每个已定义函数,都有一个内在属性[scope],其对应一个对象的列表,列表中的对象仅能内部访问。 

例如:建立一个全局函数A,那么A的[Scope]内部属性中只包含一个全局对象(Global Object),而如果我们在A中创建一个新的函数B,那么B的[Scope]属性中就包含两个对象,函数A的Activation Object(活动对象)对象在前面,全局对象(Global Object)排在后面。 

简而言之,一个函数的[Scope]属性中对象列表的顺序是上一层函数的Activation Object对象,然后是上上层的,一直到最外层的全局对象。 


第二步.执行时:当一个函数被执行的时候,会自动创建一个可以执行的对象(Execution Object),并同时绑定一个作用域链(Scope Chain)。作用域链会通过下面两个步骤来建立,用于进行标识符解析。 

1,首先,将函数对象[Scope]内部属性中的对象,按顺序复制到作用域链Scope Chain中。 
2,其次,在函数执行时,会创建一个新的Activation Object对象,这个对象中包含了this、参数(arguments)、局部变量(包括命名的参数)的定义,这个Activation Object对象会被置于作用域链的最前面。 
所以在Scope Chain中最后顺序是本函数的Activation Object,然后是上层函数的Activation Object,再上上层的Activation Object,一直到Global Object。 (如果在此方法内部定义一个函数,那么该函数的Scope就等于于Execution Object当中的Scope Chain)

 

假如声明一个全局方法Add

            function Add() {
                var a = "test"
            }

那么Add的scope(作用域链) 当中只有一个(Global Object)对象,这就是为什么在add当中可以使用Global Object对象当中的属性或者方法

JavaScript作用域链

那么在执行Add方法的时候,执行此函数时会创建一个称为“运行期上下文(execution context)”的内部对象,运行期上下文定义了函数执行时的环境。每个运行期上下文都有自己的作用域链,用于标识符解析,当运行期上下文被创建时,而它的作用域链初始化为当前运行函数的[[Scope]]所包含的对象。

即(execution context).Scope Chain = 该函数的Scope (红色1)

接下来会创建一个新的Activation Object对象,这个对象中包含了this、参数(arguments)、局部变量(包括命名的参数)的定义,这个Activation Object对象会被置于作用域链的最前面。 

JavaScript作用域链

这样一个函数的运行期作用域链就形成了。那么如果在add方法内部在声明一个方法 比如

            function add() {
                var a = "test"
                function sum() {
                }
            }

那么 sum的 Scope 就等于 add“运行期上下文(execution context)”对象的Scope Chain 



当执行js代码的过程中,遇到一个标识符,就会根据标识符的名称,在执行上下文(Execution Context)的作用域链中进行搜索。从作用域链的第一个对象(该函数的Activation Object对象)开始,如果没有找到,就搜索作用域链中的下一个对象,如此往复,直到找到了标识符的定义。如果在搜索完作用域中的最后一个对象,也就是全局对象(Global Object)以后也没有找到,则会抛出一个错误,提示undefined。 



由此而来的建议: 

1. 尽量使用局部变量,这不仅仅是涉及到私有属性的问题,局部的变量从以上过程中可以看到,能够减少搜索的时间(注:在一般的情况下,不包括浏览器的优化行为)。 

2. 避免使用with语句。因为它会修改执行上下文(Execution Context)的作用域链,在最前面添加一个对象(Variable Object)。同理,对于try-catch语句中的catch语句块也类似。

 

 

posted @ 2013-07-21 20:04  大新博客  阅读(338)  评论(0编辑  收藏  举报