数据访问之改变作用域链

来源:互联网 发布:mac鼠标右键功能 编辑:程序博客网 时间:2024/06/06 15:37
   一般来说,一个运行期上下文的作用域链不会被改变。但是,有两种表达式可以在运行时临时改变运行期上下文作用域链。第一个是with 表达式。with 表达式为所有对象属性创建一个默认操作变量。在其他语言中,类似的功能通常用来避免书写一些重复的代码。initUI()函数可以重写成如下样式:
      function initUI(){
            with (document){ //avoid!
                  var bd = body,
                  links = getElementsByTagName_r("a"),
                  i = 0,
                  len = links.length;
                  while(i < len){
                        update(links[i++]);
                  }

            getElementById("go-btn").onclick = function(){
                  start();
            };
            bd.className = "active";
            }
      }
      此重写的initUI()版本使用了一个with 表达式,避免多次书写“document”。这看起来似乎更有效率,而实际上却产生了一个性能问题。当代码流执行到一个with 表达式时,运行期上下文的作用域链被临时改变了。一个新的可变对象将被创建,它包含指定对象的所有属性。此对象被插入到作用域链的前端,意味着现在函数的所有局部变量都被推入第二个作用域链对象中,所以访问代价更高了。
      通过将document 对象传递给with 表达式,一个新的可变对象容纳了document 对象的所有属性,被插入到作用域链的前端。这使得访问document 的属性非常快,但是访问局部变量的速度却变慢了,例如bd 变量。正因为这个原因,最好不要使用with 表达式。正如前面提到的,只要简单地将document 存储在一个局部变量中,就可以获得性能上的提升。
      在JavaScript 中不只是with 表达式人为地改变运行期上下文的作用域链,try-catch 表达式的catch 子句具有相同效果。当try 块发生错误时,程序流程自动转入catch 块,并将异常对象推入作用域链前端的一个可变对象中。在catch 块中,函数的所有局部变量现在被放在第二个作用域链对象中。例如:
      try {
            methodThatMightCauseAnError();
      } catch (ex){
            alert(ex.message); //scope chain is augmented here
      }
      请注意,只要catch 子句执行完毕,作用域链就会返回到原来的状态。如果使用得当,try-catch 表达式是非常有用的语句,所以不建议完全避免。如果你计划使用一个try-catch语句,请确保你了解可能发生的错误。一个try-catch 语句不应该作为JavaScript 错误的解决办法。如果你知道一个错误会经常发生,那说明应当修正代码本身的问题。你可以通过精缩代码的办法最小化catch 子句对性能的影响。一个很好的模式是将错误交给一个专用函数来处理。例子如下:
      try {
            methodThatMightCauseAnError();
      } catch (ex){
            handleError(ex); //delegate to handler method
      }
      handleError()函数是catch 子句中运行的唯一代码。此函数以适当方法自由地处理错误,并接收由错误产生的异常对象。由于只有一条语句,没有局部变量访问,作用域链临时改变就不会影响代码的性能。Dynamic Scopes 动态作用域无论是with 表达式还是try-catch 表达式的catch 子句,以及包含()的函数,都被认为是动态作用域。一个动态作用域只因代码运行而存在,因此无法通过静态分析(察看代码结构)来确定(是否存在动态作用域)。例如:
      function execute(code) {
            (code);
            function subroutine(){
                  return window;
            }
            var w = subroutine();
            //what value is w?
      };
      execute()函数看上去像一个动态作用域,因为它使用了()。w 变量的值与code 有关。大多数情况下,w将等价于全局的window 对象,但是请考虑如下情况:execute("var window = {};")这种情况下,()在execute()函数中创建了一个局部window 变量。所以w 将等价于这个局部window 变量而不是全局的那个。所以说,不运行这段代码是没有办法了解具体情况的,标识符window 的确切含义不能预先确定。
      优化的JavaScript 引擎,例如Safari 的Nitro 引擎,企图通过分析代码来确定哪些变量应该在任意时刻被访问,来加快标识符识别过程。这些引擎企图避开传统作用域链查找,取代以标识符索引的方式进行快速查找。当涉及一个动态作用域后,此优化方法就不起作用了。引擎需要切回慢速的基于哈希表的标识符识别方法,更像传统的作用域链搜索。正因为这个原因,只在绝对必要时才推荐使用动态作用域。